Эта статья содержит простое объяснение, как работать с async/await, с краткими примерами кода. Если вы уже использовали этот способ, вряд ли вы узнаете что-то новое — прим. перев.


Что такое async/await?


Async и Await — это расширения промисов. Если последние для вас новы или понятны не до конца, прочтите мой предыдущий пост Промисы в JavaScript простым языком.



Async


Асинхронные функции позволяют нам писать код, основанный на промисах, как если бы он был синхронным, но без блокировки главного треда. Последний работает асинхронно, исполняя цикл событий. Использование async подразумевает, что функция вернёт промис (а если возвращено что-то другое, JavaScript сам обернёт его в промис).


async function firstAsync() {
  return 27;
}

firstAsync().then(alert); // 27

Выполнение этого кода приведёт к появлению модального окна со значением 27, что говорит нам, что firstAsync возвратила промис, иначе вызов .then() был бы невозможен.


Await


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


Кода демонстрирует совместное использование async и await.


async function firstAsync() {
    let promise = new Promise((res, rej) => {
        setTimeout(() => res("Now it's done!"), 1000)
    });

    // wait until the promise returns us a value
    let result = await promise; 
  
    // "Now it's done!"
    alert(result); 

};

firstAsync();

О чём следует помнить при использовании async/await


Нельзя использовать await внутри обычной функции


function firstAsync() {
  let promise = Promise.resolve(10);
  let result = await promise; // Ошибка синтаксиса
}

Вам необходимо добавить async перед объявлением firstAsync, чтобы сделать функцию асинхронной.


async/await делает исполнение последовательным


Это само по себе не плохо, просто параллельное исполнение, как правило, быстрее.


Например:


async function sequence() {
  await promise1(50); // Ждём 50 мс…
  await promise2(50); // …и только после этого ещё 50 мс
  return "done!";
}

Код выше выполнится за 100 мс, потому что операции по 50 мс выполняются последовательно. Если исполнение второй операции не зависит от результата первой, мы можем изменить код, заставив обе исполняться параллельно.


Promise.all()


Для этого используется Promise.all().


Согласно MDN:


Метод Promise.all() возвращает промис, который будет исполнен, когда все промисы переданные в итерируемом аргументе исполнены. Возвращаемый промис будет отклонен в случае и по причине отклонения первого отклонённого переданного промиса.


async function sequence() {
    await Promise.all([promise1(), promise2()]);  
    return "done!";
}

Функция promise.all() исполняет все промисы и после этого возвращает результат.


Альтернативный подход


async function parallel() {

    // Начинаем операцию на 50 мс асинхронно…
    const wait1 = promise1(50); 

    // …т.е. параллельно со второй операцией.
    const wait2 = promise2(50); 
  
    // Ждём завершения первой операции…
    await wait1; 
    
    // …что позволит получить второй результат мгновенно.
    await wait2; 
  
    return "done!";

}

Заключение


Как видите, работать с async/await несложно. В то же время, такой подход позволяет сделать код гораздо чище и понятнее, чем, например, при использовании колбэков.

Комментарии (2)


  1. mayorovp
    16.12.2019 15:14

    Использование async подразумевает, что функция вернёт промис (а если возвращено что-то другое, JavaScript сам обернёт его в промис).

    Как это соотносится с "Without knowing promises" с картинки?..


    1. germn Автор
      16.12.2019 15:27

      Смысл картинки в том, что нельзя просто так взять и использовать async/await без понимания промисов.