Async/Await — это относительно новая и продвинутая концепция JavaScript, которая была добавлена ​​в 2017 году как часть версии ECMAScript 2017 JavaScript. Но что это такое на самом деле и насколько оно отличается от методов обработки асинхронного JavaScript, существовавших раньше? Это обзор высокого уровня со ссылками на более глубокие ресурсы внизу!

Во-первых, давайте проясним несколько вещей:

Терминология. Для начинающих программистов одним из самых сложных компонентов изучения новых концепций (таких как async/await) является понимание того, к чему на самом деле относятся эти слова. Например, async/await часто называют сокращенно «асинхронным» — когда я впервые начал изучать асинхронное программирование, я сделал ошибку, предположив, что «асинхронный» === «асинхронный». Это не вариант. Асинхронный — это общий термин, относящийся к тому, как JavaScript или другие языки выполняют ваш код; «async» — это специальное ключевое слово, добавленное в собственный JavaScript в 2017 году, которое дает нам новую функциональность.

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

Промисы. Промисы относятся к ожидаемому значению, которое может быть возвращено или еще не возвращено во время выполнения нашего кода. Промис может находиться в одном из 4 различных состояний:

  • Выполнено: наше обещание вернуло значение
  • Отклонено: наше обещание не вернуло значение
  • Ожидание: обещание еще не выполнено, т. е. еще не выполнено или отклонено.
  • Урегулировано: обещание либо выполнено, либо отклонено

Функции обратного вызова.Функции в JavaScript являются «первоклассными» (т. е. вы можете вызывать другие функции внутри исходной («более высокого порядка») функции) — функции, которые вы вызываете. внутри функции более высокого порядка находятся функции «обратного вызова». До добавления промисов в JavaScript функции обратного вызова были единственным методом JavaScript для обработки асинхронных (НЕ ASYNC) функций.

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

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

В этом есть смысл. Как это новое??

Итак, асинхронная функциональность в JavaScript не нова. Он существует уже давно, но становится все более и более эффективным, действенным и удобоваримым.

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

  • Когда мы вызывали функцию внутри другой функции, эта функция становилась вложенной. Когда эта функция обратного вызова возвращала значение, если мы хотели сделать с ней что-то еще, нам приходилось вызывать вторую (а затем, возможно, третью, четвертую, десятую) вложенную функцию обратного вызова. Это стало особенно актуальным, когда мы начали использовать JavaScript для извлечения данных из API — чем глубже мы углублялись в данные, тем более ненадежной становилась наша исходная функция.
  • Это не только очень сложно для людей читать и следовать, но также создает проблемы с областью действия и обработкой ошибок. Было трудно определить, где возникла ошибка, и если мы хотели повторно использовать некоторые данные, возвращаемые из середины функции, мы не могли этого сделать, потому что новая исходная функция не могла заглянуть в глубины первой. где жили данные. Этот стиль создает то, что обычно называют «ад обратных вызовов» или «пирамида гибели». См. пример ниже старой пирамиды обратного вызова (блеф!):

Затем, в 2012 году,в JavaScript была добавлена ​​функциональность промисов. Это было здорово по нескольким причинам:

  • Обещания могут быть объединены в цепочку — это позволило нам сократить и сгладить наш асинхронный код, уменьшив наши функции обратного вызова с возможностью объединения в цепочку .then().
  • Кроме того, возможность цепочки .then() заставляла наш асинхронный код выполняться последовательно. Первый .then() возвращает значение обещания (не значение результата), которое наш второй .then() может взять и выполнить дополнительную операцию, без вложения. Это выглядит чище и понятнее, и, что более важно, решает наши проблемы с областью действия, которые были раньше.

Хорошо, но я думал, что мы пытаемся делать что-то асинхронно, зачем мне теперь хотеть, чтобы они выполнялись последовательно? Потому что мы не всегда хотим, чтобы все в нашем коде работало волей-неволей. Некоторые функции полагаются на результат другой функции, прежде чем они смогут запуститься, поэтому нам нужно, чтобы они работали последовательно, но пока они это делают, у нас могут быть другие операции, происходящие вне этой функции, которые могут продолжаться!

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

  • Наконец, это также позволяет нам использовать один .catch() для поиска ошибок вместо множества выражений «if (ошибка)», которые вы видели в примере выше.

Посмотрите пример использования обещаний и цепочки для извлечения данных из API и console.log() ниже:

Путь чище! Даже если бы у нас было 3 функции обратного вызова, как мы видели в первом примере, а не 2 здесь, это безумно легче читать и писать.

А затем в 2017 году мы подошли к добавлению async/await в JavaScript. Посмотрите тот же пример, что и выше, написанный с помощью async/await:

Стало еще чище! Что здесь происходит на самом деле?

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

Функционально этот код не настолько отличается от нашей версии с промисами и цепочками. Ключевое слово «async», которое мы видим в начале нашей функции, сообщает JavaScript, что эта функция будет асинхронной. Теперь это встроенная возможность в JavaScript.

Мы также теперь можем использовать ключевое слово «ожидание» в нашей асинхронной функции (обратите внимание, что вы не можете использовать «ожидание» глобально, только внутри асинхронной функции, по крайней мере пока), чтобы сообщить JavaScript, что нам нужно, чтобы эта функция завершилась, прежде чем мы перейдем к следующему фрагменту кода, последовательно. Await по существу заменяет .then() более понятным способом, сокращая количество подробных инструкций по возврату обещаний, которые были раньше. Он маскирует то, что наша функция возвращает и использует промисы, делая ее более похожей на синхронную конструкцию, которую людям легче читать и понимать, при этом выполняя те же самые операции под прикрытием.

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

В заключение:

Вывод здесь заключается в том, что после того, как Promises произвел революцию в том, как программисты могли обрабатывать асинхронную функциональность в языке, таком как JavaScript, который изначально был ориентирован исключительно на синхронные функции, теперь async/await повторил это улучшение и сделал асинхронную функциональность естественной в JavaScript.

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

Если вы хотите получить более глубокое представление о механике промисов и асинхронности, ознакомьтесь с некоторыми из приведенных ниже ресурсов!

Рекомендуемые чтения

Digital OceanПонимание цикла обработки событий, обратных вызовов, промисов и Async/Await в JavaScript (учебное пособие)

Mozilla Developer NetworkКак использовать промисы и Использование промисов

DevelopIntelligenceОткуда взялся Async/Await и зачем его использовать?

web.devОбещания JavaScript: введение и Асинхронные функции: дружественные обещания

dev.to7 причин, почему Async/Await в JavaScript лучше, чем простые промисы (учебник)

loginradiusОбратный вызов, обещания и асинхронное ожидание