Поиск…


Вступление

async и await строить сверху обещаний и генераторов, чтобы выражать асинхронные действия inline. Это делает асинхронный или обратный код намного проще в обслуживании.

Функции с ключевым словом async возвращают Promise и могут быть вызваны с этим синтаксисом.

Внутри async function ключевое слово await может быть применено к любому Promise и вызовет все тело функции после await , после того как обещание будет разрешено.

Синтаксис

  • асинхронная функция foo () {
    ...
    ожидание asyncCall ()
    }
  • async function () {...}
  • async () => {...}
  • (async () => {
    const data = ожидание asyncCall ()
    console.log (данные)}) ()

замечания

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

Вступление

Функция, определенная как async является функцией, которая может выполнять асинхронные действия, но все равно выглядит синхронной. То, как это делается, - это использовать ключевое слово await чтобы отложить функцию, пока она ожидает, что Promise решит или отклонит.

Примечание. Асинхронные функции - это предложение этапа 4 («Готово») на треке, которое должно быть включено в стандарт ECMAScript 2017.

Например, используя API Fetch на основе обещаний:

async function getJSON(url) {
    try {
        const response = await fetch(url);
        return await response.json();
    }
    catch (err) {
        // Rejections in the promise will get thrown here
        console.error(err.message);
    }
}

Функция async всегда возвращает сам Promise, поэтому вы можете использовать его в других асинхронных функциях.

Стиль функции стрелки

const getJSON = async url => {
    const response = await fetch(url);
    return await response.json();
}

Меньше отступа

С обещаниями:

function doTheThing() {
    return doOneThing()
        .then(doAnother)
        .then(doSomeMore)
        .catch(handleErrors)
}

С асинхронными функциями:

async function doTheThing() {
    try {
        const one = await doOneThing();
        const another = await doAnother(one);
        return await doSomeMore(another);
    } catch (err) {
        handleErrors(err);
    }
}

Обратите внимание, как возврат находится внизу, а не вверху, и вы используете встроенную механику обработки ошибок ( try/catch ).

Ожидание и приоритет оператора

Вы должны учитывать приоритет оператора при использовании ключевого слова await .

Представьте, что у нас есть асинхронная функция, которая вызывает другую асинхронную функцию getUnicorn() которая возвращает Promise, которая разрешает экземпляр класса Unicorn . Теперь мы хотим получить размер единорога, используя метод getSize() этого класса.

Посмотрите на следующий код:

async function myAsyncFunction() {
    await getUnicorn().getSize();
}

На первый взгляд это кажется правильным, но это не так. Из-за приоритета оператора это эквивалентно следующему:

async function myAsyncFunction() {
    await (getUnicorn().getSize());
}

Здесь мы пытаемся вызвать getSize() объекта Promise, чего мы не хотим.

Вместо этого мы должны использовать скобки для обозначения того, что сначала хотим дождаться единорога, а затем вызвать getSize() результата:

async function asyncFunction() {
    (await getUnicorn()).getSize();
}

Конечно. предыдущая версия может быть действительной в некоторых случаях, например, если getUnicorn() была синхронной, но метод getSize() был асинхронным.

Асинхронные функции по сравнению с обещаниями

async функции не заменяют тип Promise ; они добавляют ключевые слова языка, которые облегчают вызов. Они взаимозаменяемы:

async function doAsyncThing() { ... }

function doPromiseThing(input) { return new Promise((r, x) => ...); }

// Call with promise syntax
doAsyncThing()
    .then(a => doPromiseThing(a))
    .then(b => ...)
    .catch(ex => ...);

// Call with await syntax
try {
    const a = await doAsyncThing();
    const b = await doPromiseThing(a);
    ...
}
catch(ex) { ... }

Любая функция, использующая цепочки обещаний, может быть переписана с помощью await :

function newUnicorn() {
  return fetch('unicorn.json')                     // fetch unicorn.json from server
  .then(responseCurrent => responseCurrent.json()) // parse the response as JSON
  .then(unicorn =>
    fetch('new/unicorn', {                         // send a request to 'new/unicorn' 
        method: 'post',                            // using the POST method
        body: JSON.stringify({unicorn})            // pass the unicorn to the request body
    })
  )
  .then(responseNew => responseNew.json())
  .then(json => json.success)                      // return success property of response
  .catch(err => console.log('Error creating unicorn:', err));
 }

Функция может быть переписана с использованием async / await следующим образом:

async function newUnicorn() {
  try {
    const responseCurrent = await fetch('unicorn.json'); // fetch unicorn.json from server
    const unicorn = await responseCurrent.json();        // parse the response as JSON
    const responseNew = await fetch('new/unicorn', {     // send a request to 'new/unicorn'
      method: 'post',                                    // using the POST method
      body: JSON.stringify({unicorn})                    // pass the unicorn to the request body
    });
    const json = await responseNew.json();
    return json.success                                  // return success property of response
  } catch (err) {
    console.log('Error creating unicorn:', err);
  }
}

Этот async вариант newUnicorn() кажется, возвращает Promise , но на самом деле было несколько await ключевых слов. Каждый из них возвратил Promise , так что на самом деле у нас была коллекция обещаний, а не цепочка.

На самом деле мы можем думать об этом как о генераторе function* , каждый из которых await yield new Promise . Тем не менее, результаты каждого обещания необходимы для продолжения функции. Вот почему необходимо дополнительное ключевое слово async для функции (а также ключевое слово await при вызове обещаний), поскольку оно сообщает Javascript автоматически создавать наблюдателя для этой итерации. Promise возвращенная async function newUnicorn() решает, когда эта итерация завершается.

Практически вам не нужно это учитывать; await скрывает обещание и async скрывает итерацию генератора.

Вы можете вызывать функции async как если бы они были обещаниями, и await каких-либо обещаний или любой async функции. Вам не нужно await асинхронной функции, так же, как вы можете выполнить обещание без .then() .

Вы также можете использовать async IIFE, если вы хотите немедленно выполнить этот код:

(async () => {
  await makeCoffee()
  console.log('coffee is ready!')
})()

Цикл с асинхронным ожиданием

При использовании async ждут в циклах, вы можете столкнуться с некоторыми из этих проблем.

Если вы просто попытаетесь использовать ожидание внутри forEach , это вызовет Unexpected token ошибку Unexpected token .

(async() => {
 data = [1, 2, 3, 4, 5];
 data.forEach(e => {
   const i = await somePromiseFn(e);
   console.log(i);
 });
})();

Это происходит из-за того, что вы ошибочно видели функцию стрелки как блок. await будет в контексте функции обратного вызова, которая не является async .
Интерпретатор защищает нас от создания вышеуказанной ошибки, но если вы добавите async для обратного вызова forEach ошибки не будут выбрасываться. Вы можете подумать, что это решает проблему, но она не будет работать должным образом.

Пример:

(async() => {
  data = [1, 2, 3, 4, 5];
  data.forEach(async(e) => {
    const i = await somePromiseFn(e);
    console.log(i);
  });
  console.log('this will print first');
})();

Это происходит потому, что функция async callback может только приостанавливаться, а не родительская асинхронная функция.

Вы можете написать функцию asyncForEach, которая возвращает обещание, и тогда вы можете что-то вроде await asyncForEach(async (e) => await somePromiseFn(e), data ) В основном вы возвращаете обещание, которое разрешается, когда все обратные вызовы ожидаются и выполняются. Но есть лучшие способы сделать это, и это просто использовать цикл.

Вы можете использовать for-of цикла или ее for/while во for/while цикла, это действительно не имеет значения , какой вы выбираете.

(async() => {
  data = [1, 2, 3, 4, 5];
  for (let e of data) {
    const i = await somePromiseFn(e);
    console.log(i);
  }
  console.log('this will print last');
})();

Но есть еще один улов. Это решение будет ожидать завершения каждого вызова для somePromiseFn перед повторением следующего.
Это здорово, если вы действительно хотите, чтобы ваши вызовы somePromiseFn исполнялись по порядку, но если вы хотите, чтобы они запускались одновременно, вам нужно будет await на Promise.all .

(async() => {
 data = [1, 2, 3, 4, 5];
 const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
 console.log(...p);
})();

Promise.all получает массив обещаний в качестве единственного параметра и возвращает обещание. Когда все обещания в массиве будут решены, возвращенное обещание также будет разрешено. Мы await этого обещания, и когда он будет разрешен, все наши ценности доступны.

Вышеприведенные примеры полностью выполняются. Функция somePromiseFn может быть выполнена как функция асинхронного эха с тайм-аутом. Вы можете попробовать примеры в Babel-repl, по крайней мере, с предустановленной stage-3 и посмотреть на выход.

function somePromiseFn(n) {
 return new Promise((res, rej) => {
   setTimeout(() => res(n), 250);
 });
}

Одновременные асинхронные (параллельные) операции

Часто вы хотите выполнять асинхронные операции параллельно. Существует прямой синтаксис , который поддерживает это в async / await предложений, но так как await будет ждать обещания, вы можете обернуть несколько обещаний вместе в Promise.all ждать их:

// Not in parallel

async function getFriendPosts(user) {
    friendIds = await db.get("friends", {user}, {id: 1});
    friendPosts = [];
    for (let id in friendIds) {
        friendPosts = friendPosts.concat( await db.get("posts", {user: id}) );
    }
    // etc.
}

Это будет делать каждый запрос для регулярного получения сообщений каждого друга, но они могут выполняться одновременно:

// In parallel

async function getFriendPosts(user) {
    friendIds = await.db.get("friends", {user}, {id: 1});
    friendPosts = await Promise.all( friendIds.map(id => 
      db.get("posts", {user: id})
    );
    // etc.
}

Это приведет к переходу по списку идентификаторов, чтобы создать массив обещаний. await будет ждать все обещает быть полным. Promise.all объединяет их в единое обещание, но они выполняются параллельно.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow