Начало работы с асинхронным 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()
, но не ждет, пока он выполнит свое обещание.
Порядок действий можно понять следующим образом:
- Функция начинается с вызова
await sayHello()
. Это говорит JavaScript ждать, покаsayHello()
выполнит свое обещание, прежде чем переходить к остальной части кода. - Когда начинается
sayHello()
, сначала выполняетсяawait timeout(4000)
. По прошествии четырех секунд он печатает «Привет!», Выполняя свое обещание. - Теперь, когда
sayHello()
закончил,sayHelloAndGreet()
может продолжить выполнение кода. Следующая строка invokesgreeting()
. Однако, поскольку нетawait
передgreeting()
, он не ждет завершенияgreeting()
и интерпретируетgreeting()
какPromise {pending}
. (Помните, что это не означает, чтоgreeting()
перестает выполняться!) - Когда все завершено в
sayHelloAndGreet()
, выполняется следующий.then
, распечатывающий «Готово сказать привет и приветствие!». - Однако
greeting()
все еще выполняется в фоновом режиме. По завершении он печатает «Как дела?» к консоли.