Начало работы с асинхронным JavaScript

Когда я учился на Flatiron, один из моих проектов включал создание одностраничного приложения, в котором использовалась внутренняя часть Rails API с интерфейсом JavaScript, CSS и HTML. В итоге я сделал (упрощенную) версию корейской карточной игры Хвату, также известной как Ханафуда на японском языке. В начале проекта я мало знал, что скоро буду нырять с головой в мир async / await. Надеюсь, знания, которые я получил в ходе своей борьбы, будут полезны будущим студентам и начинающим программистам.

Что такое Async / Await?

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

const someAsyncFunction = async () => {
  console.log('Hello!');
};

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

Асинхронный режим нельзя использовать с .forEach

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

.reduce позволяет выполнять итерацию по массиву, вызывать функцию async для данного элемента, ждать ее разрешения и затем продолжать итерацию.

Await не блокирует стек выполнения

await только приостанавливает выполнение кода внутри функции, в которой он использовался. Он не блокирует весь стек вызовов. Что это значит? Давайте посмотрим, рефакторинг первого блока кода.

Вас удивляет порядок вывода? Это произошло потому, что await не использовался перед вызовом greeting() в sayHelloAndGreet(). Поскольку await нет, sayHelloAndGreet() просто вызывает greeting(), но не ждет, пока он выполнит свое обещание.

Порядок действий можно понять следующим образом:

  1. Функция начинается с вызова await sayHello(). Это говорит JavaScript ждать, пока sayHello() выполнит свое обещание, прежде чем переходить к остальной части кода.
  2. Когда начинается sayHello(), сначала выполняется await timeout(4000). По прошествии четырех секунд он печатает «Привет!», Выполняя свое обещание.
  3. Теперь, когда sayHello() закончил, sayHelloAndGreet() может продолжить выполнение кода. Следующая строка invokes greeting(). Однако, поскольку нет await перед greeting(), он не ждет завершения greeting() и интерпретирует greeting() как Promise {pending}. (Помните, что это не означает, что greeting() перестает выполняться!)
  4. Когда все завершено в sayHelloAndGreet(), выполняется следующий .then, распечатывающий «Готово сказать привет и приветствие!».
  5. Однако greeting() все еще выполняется в фоновом режиме. По завершении он печатает «Как дела?» к консоли.