Почему код после ожидания не запускается сразу? Разве это не должно быть неблокирующим?

Мне сложно понять, как async и await работают за кулисами. Я знаю, что у нас есть обещания, которые делают наш код неблокирующим, с помощью функции then мы можем разместить всю необходимую работу после выполнения обещания. и работу, которую мы хотим выполнять параллельно, чтобы пообещать, что мы просто напишем ее вне нашей функции then. Следовательно, код становится неблокирующим. Однако я не понимаю, как async await делает неблокирующий код.

async function myAsyncFunction() {
  try {
    let data = await myAPICall('https://jsonplaceholder.typicode.com/posts/1');
    // It will not run this line until it resolves await.
    let result = 2 + 2;
    return data;
  }catch (ex){
    return ex;
  }
}

См. Приведенный выше код. Я не могу двигаться дальше, пока вызов API не будет разрешен. Если он заставляет мой код блокировать код, чем он лучше обещаний? Или я что-то упустил по async и await? Где я могу разместить свой код, не зависящий от вызова ожидания? чтобы он мог продолжать работать, не дожидаясь выполнения?

Я добавляю код обещания, который хотел бы воспроизвести в примере async await.

function myPromiseAPI() {
  myAPICall('https://jsonplaceholder.typicode.com/posts/1')
    .then(function (data) {
        // data
    });
   // runs parallel
  let result = 2 + 2;
}

person Rohail Najam    schedule 09.04.2017    source источник
comment
AFAIK, await требует, чтобы то, что он ожидает, было обещанием. Так что на самом деле это не вариант "или-или".   -  person Paul    schedule 09.04.2017
comment
Это как если бы строки после вашего, пока он не разрешил комментарий, находятся внутри .then(). И вся ваша функция является асинхронной, поэтому при любых вызовах не нужно ее ждать.   -  person nnnnnn    schedule 09.04.2017
comment
@Paul также преобразует выражение в обещание, если оно не является обещанием. то, что я читаю в блоге   -  person Rohail Najam    schedule 09.04.2017
comment
@nnnnnn Да. это как будто я вставил это в .then (). но как мне поместить его за пределы then () в сценарии async await   -  person Rohail Najam    schedule 09.04.2017
comment
@RohailNajam Верно, я все же имел в виду асинхронные вызовы. Использование функции, которая ожидает обратного вызова вместо этого обещания, не сработает.   -  person Paul    schedule 09.04.2017


Ответы (1)


Как следует из названия, ключевое слово await заставит функцию «ждать», пока ее обещание не разрешится, перед выполнением следующей строки. Весь смысл await в том, чтобы заставить код дождаться завершения операции перед продолжением.

Разница между этим и блокирующим кодом состоит в том, что мир вне функции может продолжать выполнение, пока функция ожидает завершения асинхронных операций.

async и await - это просто синтаксический сахар поверх обещаний. Они позволяют писать код, очень похожий на обычный синхронный код, даже если он использует скрытые обещания. Если мы переведем ваш пример на что-то, что явно работает с обещаниями, это будет выглядеть примерно так:

function myAsyncFunction() {
  return myAPICall('https://jsonplaceholder.typicode.com/posts/1')
    .then(function (data) {
       let result = 2 + 2;
       return data;
    })
    .catch(function (ex) {
        return ex;
    });
}

Как мы видим здесь, строка let result = 2 + 2; находится внутри обработчика .then(), что означает, что она не будет выполняться, пока myAPICall() не будет разрешен. То же самое и с await. await просто абстрагирует .then() для вас.

Следует иметь в виду (и я думаю, что то, что вы ищете), что вам не нужно сразу использовать await. Если бы вы написали свою функцию вот так, то вы могли бы сразу выполнить строку let result = 2 + 2;:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() {
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');
}

async function myAsyncFunction() {
  try {
    console.log('starting');

    // just starting the API call and storing the promise for now. not waiting yet
    let dataP = myAPICall('https://jsonplaceholder.typicode.com/posts/1');

    let result = 2 + 2;

    // Executes right away
    console.log('result', result);

    // wait now
    let data = await dataP;

    // Executes after one second
    console.log('data', data);

    return data;
  } catch (ex) {
    return ex;
  }
}

myAsyncFunction();

После некоторого разъяснения я вижу, что вы действительно хотели знать, как избежать необходимости ждать двух асинхронных операций одну за другой и вместо этого заставить их выполняться параллельно. В самом деле, если вы используете один await за другим, второй не начнет выполняться, пока не завершится первый:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() {
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');
}

async function myAsyncFunction() {
  try {
    console.log('starting');

    let data1 = await myAPICall('https://jsonplaceholder.typicode.com/posts/1');
    // logs after one second
    console.log('data1', data1);

    let data2 = await myAPICall('https://jsonplaceholder.typicode.com/posts/2');
    // logs after one more second
    console.log('data2', data2);
  } catch (ex) {
    return ex;
  }
}

myAsyncFunction();

Чтобы избежать этого, вы можете запустить обе асинхронные операции, выполнив их, не ожидая их, назначив их обещания некоторым переменным. Тогда вы можете дождаться обоих обещаний:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() {
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');
}

async function myAsyncFunction() {
  try {
    console.log('starting');
    // both lines execute right away
    let dataP1 = myAPICall('https://jsonplaceholder.typicode.com/posts/1');
    let dataP2 = myAPICall('https://jsonplaceholder.typicode.com/posts/2');

    let data1 = await dataP1;
    let data2 = await dataP2;

    // logs after one second
    console.log('data1', data1);
    console.log('data2', data2);
  } catch (ex) {
    return ex;
  }
}

myAsyncFunction();

Один из альтернативных способов сделать это - использовать Promise.all() с некоторой декомпозицией массива:

const timeout = 
    seconds => new Promise(res => setTimeout(res, seconds * 1000));

function myAPICall() {
  // simulate 1 second wait time
  return timeout(1).then(() => 'success');
}

async function myAsyncFunction() {
  try {
    console.log('starting');

    // both myAPICall invocations execute right away
    const [data1, data2] = await Promise.all([
        myAPICall('https://jsonplaceholder.typicode.com/posts/1'), 
        myAPICall('https://jsonplaceholder.typicode.com/posts/2'),
    ]);

    // logs after one second
    console.log('data1', data1);
    console.log('data2', data2);
  } catch (ex) {
    return ex;
  }
}

myAsyncFunction();

person JLRishe    schedule 09.04.2017
comment
Это я поняла. Я хочу сказать, что если мы хотим запустить что-либо параллельно с этим вызовом api, мы просто поместим это рядом с функцией then, как я сделал в моем отредактированном вопросе. Как мне достичь этого параллелизма в async await - person Rohail Najam; 09.04.2017
comment
@RohailNajam Пожалуйста, посмотрите мое обновление. Вы можете запускать столько обещаний параллельно, сколько хотите. Как только вы используете await, функция начнет ждать их результата. - person JLRishe; 09.04.2017
comment
о, я этого не знал. Спасибо. - person Rohail Najam; 09.04.2017
comment
let results = await Promise.all([dataP1, dataP2]); вернет массив, содержащий результаты обоих данных.? - person Rohail Najam; 09.04.2017
comment
@RohailNajam Да, это правильно. Я изменил свой ответ, чтобы показать, как можно использовать декомпозицию массива, чтобы разбить его на соответствующие значения. - person JLRishe; 09.04.2017
comment
Спасибо за помощь - person Rohail Najam; 09.04.2017
comment
мир за пределами функции будет продолжать выполняться, пока эта функция выполняет свое дело. Внешний мир будет продолжаться, пока функция ничего не делает. - person a better oliver; 12.04.2017
comment
@zeroflagL Да, придирка, но честно. Исправил формулировку. - person JLRishe; 03.11.2017