Эта статья будет руководством по написанию промисов, использованию их через async/await код и использованию их в циклах.

В качестве примечания, в этой статье предполагается, что вы уже знакомы с такими понятиями, как блокирующий и неблокирующий код в JavaScript, и, возможно, даже видели (или писали) промис раньше.

Если вы заинтересованы в более глубоком изучении промисов, обязательно ознакомьтесь с официальной документацией по промисам Mozilla на веб-сайте MDN.

Пример, на который мы будем опираться

Для тех, кто хочет сразу перейти к заключению, вот пример, к которому мы строим:

Классический способ отложить выполнение кода

Отличным местом для иллюстрации преимуществ Promises в JavaScript является использование классической встроенной функции под названием setTimeout.

Эта функция принимает два параметра (первый — это функция, которую вы хотите выполнить, а второй — время в миллисекундах) и задерживает выполнение функции на указанное время:

setTimeout(inputFunction, timeInMilliseconds);

Программисты также часто используют анонимную функцию в качестве inputFunction:

Будет выведено следующее:

1 | print immediately
2 | print after one second

Где строка 2 на самом деле будет выводиться в консоль через одну секунду.

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

В одном случае мы вручную меняем значение времени на 2000 во втором вызове setTimeout, чтобы имитировать выполнение строки 3 через одну секунду после строки 2:

По-другому мы вкладываем первый setTimeout в другой setTimeout с таким же значением времени:

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

Давайте обещаем нашу любимую функцию setTimeout, и вы увидите, что печатать 100 строк одну секунду за другой не так уж и сложно.

Объявление обещания

Доступ к промису и его создание не осуществляется с использованием зарезервированного слова, это скорее объект, который должен быть создан с помощью встроенного класса Promise. В случае обещания нашей функции setTimeout мы можем объявить новую функцию с именем delay следующим образом:

Здесь наша новая функция delay возвращает объект Promise. Обещание принимает function в качестве первого параметра и фиксирует два значения resolve и reject внутри него (которые также являются функциями, как мы рассмотрим ниже).

До сих пор мы сохраняли простоту, но давайте предположим, что мы выполняем код, который может дать сбой или сбой, или вызвать какую-то ошибку в нашем delayedFunction, давайте улучшим наш пример с некоторой базовой обработкой ошибок и добавим блоки try / catch. Это позволит нам использовать reject(...):

Теперь у нас есть функция delay на основе промисов, в которую мы можем передавать функции. Когда мы вызываем delay, мы последуем вызовом метода, доступного в нашем объекте обещания с именем .then(...), давайте попробуем его:

Как и в нашем предыдущем примере, это также выводит:

1 | print immediately
2 | print me after one second

С точным временем, которое подразумевают журналы.

Метод .then(...) выполняется с использованием УСПЕШНО возвращенных результатов метода delayedFunction. Мы видим, как их кормят в resolve(...). У нас также есть еще один метод под названием .catch(...), который мы можем использовать для обработки отклоненных обещаний.

Теперь это выведет:

1 | print immediately
This is a fake error

Здесь мы намеренно выбрасываем новый Error с сообщением об ошибке This is a fake error. Как видите, мы можем поймать его идеально.

Это все еще не полностью решает нашу проблему. Если мы хотим выполнить кучу delay функций одну за другой, нам все равно придется иметь дело со следующим:

Это становится действительно трудно читать по мере роста сложности наших задач (это также известно как CALLBACK HELL). Вот где появляется async / await.

Асинхронно / Ожидание

Короче говоря, если что-то возвращает объект Promise, вы можете использовать с ним async / await и избежать ада обратных вызовов. Давайте преобразуем наш предыдущий пример, чтобы проиллюстрировать преимущества.

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

Теперь давайте используем await для вызова нашей функции задержки соответствующим образом:

И мы получаем ожидаемый результат:

1 | print immediately
2 | print after one second

Теперь добавим еще несколько строк:

И мы получаем:

Что делает await, так это заменяет

с участием

Таким образом, мы можем преобразовать следующее:

в

Гораздо чище, не так ли?

Асинхронный/ожидание в циклах

Может быть несколько способов объединения async / await с итерацией, в этой статье мы рассмотрим только два основных способа:

Использование for loop

Вы можете очень легко использовать await внутри цикла for ... of:

Использование while loop

Вы также можете использовать простой цикл while:

И вот оно. Спасибо за чтение и удачного кодирования!