Интересное объяснение async / await в JavaScript

От автора: в JavaScript вы можете кодировать асинхронные задачи тремя способами. Давайте посмотрим на эти варианты.

Первый подход — использование обратных вызовов. Когда асинхронная операция была завершена, выполняется функция обратного вызова (то есть вызовите меня, когда операция будет завершена):

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

Промис — это объект-заполнитель для результатов асинхронной задачи. С помощью промисов вы можете легче справляться с асинхронными операциями.

Вы видели цепочки промисов .then().then()…then()..? Проблема промисов — их многословность. Наконец, третий вариант — это синтаксис async/await (начиная с ES2017). Это позволяет вам писать асинхронный код в сжатой и синхронной манере:

В этом посте я объясню, шаг за шагом, как использовать async/await в JavaScript.

Примечание: async/await – это синтаксический сахар поверх промисов. Я рекомендую ознакомиться с промисами, прежде чем продолжить.

Синхронное добавление

Поскольку в названии поста упоминается интересное объяснение, я собираюсь постепенно объяснять историю async/await. Начнем с простой (синхронной) функции, задача которой рассчитать прибавку к зарплате:

increaseSalary() — это функция, которая суммирует 2 числа. n1 + n2 это синхронная операция.

Начальник не хочет быстро повышать зарплату сотруднику. Таким образом, вам не разрешено использовать оператор сложения функции + в increaseSalary().

Вместо этого вы должны использовать медленную функцию, которая требует 2 секунды для суммирования чисел. Назовем функцию slowAddition():

slowAddition() возвращает промис, который разрешается в сумму аргументов после задержки в 2 секунды. Как обновить функцию increaseSalary() для поддержки медленного добавления?

Асинхронное добавление

Первая наивная попытка — заменить выражение n1 + n2 вызовом slowAddition(n1, n2):

К сожалению, функция increaseSalary() не умеет обрабатывать промисы. Функция рассматривает промисы как обычные объекты: она не знает, как и когда извлекать значения из промисов.

Теперь самое время узнать, как increaseSalary() обрабатывает промис, возвращаемый slowAddition() с помощью синтаксиса async/await.

Во-первых, вам нужно добавить ключевое слово async рядом с объявлением функции. Затем внутри тела функции вам нужно использовать оператор await, чтобы функция ожидала выполнения промиса. Давайте внесем эти изменения в работу increaseSalary():

JavaScript оценивает const newSalary = await slowAddition(base, increase) следующим образом:

await slowAddition(base, increase) приостанавливает выполнение функции increaseSalary()

Через 2 секунды промис, возвращаемый пользователем slowAddition(base, increase), разрешается.

Возобновляется выполнение функции increaseSalary()

newSalary присваивается разрешенное значение промиса 1200 (1000+200)

Выполнение функции продолжается в обычном режиме.

Проще говоря, когда JavaScript встречается await promise в функции async, он приостанавливает выполнение функции до тех пор, пока промис не будет выполнен. Разрешенное значение промиса становится результатом оценки await promise.

Даже несмотря на то, что return newSalary возвращает число 1200, если вы посмотрите на фактическое значение, возвращаемое функцией increaseSalary(1000, 200) — это все равно промис!

Функция async всегда возвращает промис, который разрешается в значение внутри тела функции return value:

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

Ошибка асинхронного добавления

Несправедливо, что начальник поставил требование медленно повышать зарплату. Вы решили саботировать функцию slowAddition(). Вы изменяете функцию медленного добавления, чтобы отклонить добавление чисел:

Как обработать отклоненный промис внутри асинхронной функции calculateSalary()? Просто заключите оператор await в блок try/catch:

В выражении await slowAdditionBroken(base, increase) JavaScript приостанавливает выполнение функции и ждет, пока промис не будет выполнен (промис успешно разрешен) или не будет отклонен (произошла ошибка).

Через 3 секунды промис отклоняется new Error(‘Unable to sum numbers’). Из-за отклонения выполнение функции переходит в условие catch (e){ }, в котором базовый оклад умножается на 2.

Скупой платит дважды. Теперь босс должен платить двойную зарплату. Хорошо!

Если вы не перехватите отклоненный промис, ошибка распространяется, и промис, возвращаемый функцией async, отклоняется:

Вложенные асинхронные функции

Несмотря на выражение return <value> внутри асинхронной функции, возвращающее значение является полезной нагрузкой, а не промисом, тем не менее, когда асинхронная функция вызывается, она возвращает промис. Это хорошо, потому что вы можете вкладывать асинхронные функции!

Например, напишем асинхронную функцию, которая увеличивает массив зарплат с помощью функции slowAddition():

await salaryIncrease(baseSalary, increase) вызывается 3 раза для каждой зарплаты в массиве. Каждый раз JavaScript ждет 2 секунды, пока не будет вычислена сумма. Таким образом вы можете вкладывать функции async в функции async.

Параллельный async

В предыдущем примере добавления массива зарплат добавление происходит последовательно: функция приостанавливается на 2 секунды для каждой зарплаты.

Но можно повышать зарплату параллельно! Давайте воспользуемся функцией Promise.all(), чтобы начать все повышения зарплаты одновременно:

Задачи по увеличению зарплаты начинаются сразу (await не используется рядом с increaseSalary(baseSalary, increase)) и у нас накапливаются промисы salariesPromises.

await Promise.all(salariesPromises) затем приостанавливает выполнение функции до тех пор, пока все асинхронные операции, обрабатываемые параллельно, не завершатся. Наконец, только через 2 секунды переменная newSalaries будет содержать увеличенные зарплаты.

Вам удалось увеличить зарплату всем сотрудникам всего за 2 секунды, даже если каждая операция выполняется медленно и требует 2 секунды. Вы снова обманули босса!

Практический пример async

Обычная ситуация, когда вы хотите использовать синтаксис async/await — это получение удаленных данных.

Метод fetch() — хороший кандидат для использования async/await, потому что он возвращает промис, который разрешается в значение, возвращаемое удаленным API.

Например, вот как можно получить список фильмов с удаленного сервера:

await fetch(‘//api.example.com/movies’) собирается приостановить выполнение fetchMovies(), пока запрос не будет завершен. Затем вы можете извлечь фактическое использование await response.json().

Заключение

async/await является синтаксическим сахаром поверх промисов и обеспечивает способ синхронной обработки асинхронных задач. async/await имеет 4 простых правила:

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

Оператор await promise приостанавливает выполнение функции до тех пор, пока promise не будет успешно разрешен или отклонен.

Если promise разрешен успешно, то оператор await возвращает разрешенное значение: const resolvedValue = await promise. В противном случае отклоненный промис можно перехватить внутри try/catch.

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

Тест: является ли ошибкой ожидание примитивных значений, например await 3?

Автор: Dmitri Pavlutin

Источник: //dmitripavlutin.com

Редакция: Команда webformyself.

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии запрещены.