Содержание
На заре Интернета веб-сайты часто состояли из статических данных на странице HTML . Но теперь, когда веб-приложения стали более интерактивными и динамичными, становится все более необходимо выполнять интенсивные операции, такие как выполнение внешних сетевых запросов для получения данных API . Для обработки этих операций в JavaScript разработчик должен использовать методы асинхронного программирования (цикл событий, обратных вызовов, промисов и Async/Await).
Поскольку JavaScript — это однопоточный язык программирования с моделью синхронного выполнения, который обрабатывает по очереди одну операцию за другой, он может обрабатывать только один оператор за раз. Однако такое действие, как запрос данных из API, может занять неопределенное время в зависимости от размера запрашиваемых данных, скорости сетевого подключения и других факторов. Если бы вызовы API выполнялись синхронно, браузер не смог бы обрабатывать какой-либо пользовательский ввод, например прокрутку или нажатие кнопки, до завершения этой операции. Это называется блокировкой.
Чтобы предотвратить блокирующее поведение, в среде браузера есть множество асинхронных веб-API, к которым JavaScript может получить доступ, то есть они могут выполняться параллельно с другими операциями, а не последовательно. Это полезно, поскольку позволяет пользователю продолжать использовать браузер в обычном режиме, пока обрабатываются асинхронные операции.
Как разработчик JavaScript, вы должны знать, как работать с асинхронными веб-API и обрабатывать ответ или ошибку этих операций. В этой статье мы разберемся, что такое цикл событий, попробуем оригинальный способ работы с асинхронным поведением с помощью обратных вызовов, обновленном добавлении промисов в ECMAScript 2015, и современную практику использования async/await
.
Эта статья посвящена клиентскому JavaScript в среде браузера. Те же концепции обычно верны и в среде Node.js , однако Node.js использует свои собственные API C ++, а не веб-API браузера.
Цикл событий
Рассмотрим, как JavaScript обрабатывает асинхронный код с помощью цикла событий. Сначала он продемонстрирует работу цикла событий, а затем объяснит два элемента цикла событий: стек и очередь.
Код JavaScript, не использующий асинхронные веб-API, будет выполняться синхронно — по одному, последовательно. Это демонстрируется в следующем примере кода, который вызывает три функции, каждая из которых выводит номер на консоль:
// Определите три примера функций function first() { console.log(1) } function second() { console.log(2) } function third() { console.log(3) }
В этом коде вы определяете три функции, с помощью которых печатаются числа console.log()
.
Далее напишите вызовы функций:
// Выполнить функции first() second() third()
Вывод будет основан на порядке вызова функций — first()
, second()
, то third()
:
Output 1 2 3
Когда используется асинхронный веб-API, правила усложняются. Это можно проверить с помощью встроенного API setTimeout
, который устанавливает таймер и выполняет действие по прошествии определенного времени. setTimeout
должен быть асинхронным, иначе весь браузер будет зависать во время ожидания, что приведет к ухудшению взаимодействия с пользователем.
Добавьте setTimeout
в second
функцию для имитации асинхронного запроса:
// Определить три функции-примера, но одна из них содержит асинхронный код function first() { console.log(1) } function second() { setTimeout(() => { console.log(2) }, 0) } function third() { console.log(3) }
setTimeout
принимает два аргумента: функцию, которую она будет запускать асинхронно, и время ожидания перед вызовом этой функции. В этом коде мы обернули console.log
анонимной функцией и передали ее setTimeout
, а затем задали запуск функции через 0
миллисекунд.
Теперь вызовите функции, как и раньше:
// Выполнить функции first() second() third()
Вы можете ожидать, что при установке setTimeout
на 0
выполнение этих трех функций будут выводиться в последовательном порядке. Но поскольку это асинхронно, функция с таймаутом будет напечатана последней:
Output 1 3 2
На самом деле неважно, установите ли вы тайм-аут на ноль секунд или пять минут — console.log
, вызываемый асинхронным кодом, будет выполняться после синхронных функций верхнего уровня. Это происходит потому, что среда хоста JavaScript, в данном случае браузер, использует концепцию, называемую циклом событий, для обработки параллелизма или параллельных событий. Поскольку JavaScript может выполнять только один оператор за раз, ему нужно, чтобы цикл обработки событий был проинформирован о том, когда выполнять конкретный оператор. Цикл событий обрабатывает это с помощью концепций стека и очереди.
Стек
Стек, или стек вызовов, хранит состояние того, какая функция в настоящее время выполняется. Если вы не знакомы с концепцией стека, вы можете представить его как массив со свойствами «Последний пришел — первым ушел» (LIFO), что означает, что вы можете добавлять или удалять элементы только из конца стека. JavaScript запустит текущий фрейм (или вызов функции в определенной среде) в стеке, затем удалит его и перейдет к следующему.
Для примера, содержащего только синхронный код, браузер обрабатывает выполнение в следующем порядке:
- Добавляем
first()
в стек, запускаемfirst(),
который выводит 1 на консоль, удаляемfirst()
из стека. - Добавляем
second()
в стек, запускаемsecond(),
который выводит 2 на консоль, удаляемsecond()
из стека. - Добавляем
third()
в стек, запускаемthird(),
который выводит 3 на консоль, удаляемthird()
из стека.
Второй пример с setTimout
выглядит так:
- Добавляем
first()
в стек, запускаемfirst(),
который выводит 1 на консоль
, удаляем
first()
из стека. - Добавляем
second()
в стек, запускаемsecond()
.- Добавляем
setTimeout()
в стек, запускаемsetTimeout()
веб-API, который запускает таймер и добавляет анонимную функцию в очередь , удаляетsetTimeout()
из стека.
- Добавляем
- Удаляем
second()
из стека. - Добавляем
third()
в стек, запускаемthird(),
который выводит 3 на консоль, удаляемthird()
из стека. - Цикл событий проверяет очередь на наличие ожидающих сообщений и находит анонимную функцию из
setTimeout()
, добавляет функцию в стек, которая записывается2
в консоль, а затем удаляет ее из стека.
Использование асинхронного веб-API setTimeout
знакомит с концепцией очереди , которую рассмотрим далее.
Очередь
Очередь, также называемая очередью сообщений или очередью задач, является областью ожидания для функций. Когда стек вызовов пуст, цикл обработки событий проверяет очередь на наличие ожидающих сообщений, начиная с самого старого сообщения. Как только он найдет его, он добавит его в стек, который выполнит функцию, указанную в сообщении.
В примере setTimeout
анонимная функция запускается сразу после остальной части выполнения верхнего уровня, поскольку таймер был установлен на 0 секунд. Важно помнить, что таймер не означает, что код будет выполняться ровно через 0 секунд или в любое другое время, но что он добавит анонимную функцию в очередь за это время. Эта система очереди существует, потому что, если бы таймер добавлял анонимную функцию непосредственно в стек, когда таймер заканчивает работу, он прервал бы любую выполняемую в данный момент функцию, что могло бы иметь непредвиденные и непредсказуемые последствия.
Примечание. Существует еще одна очередь, называемая очередью заданий или очередью микрозадач, которая обрабатывает обещания. Микрозадачи, такие как промисы, обрабатываются с более высоким приоритетом, чем макрозадачи, такие как setTimeout
.
Мы разобрались, как обрабатывается цикл событий при помощи использования стека и очереди для обработки порядка выполнения кода. Следующая задача — выяснить, как контролировать порядок выполнения в вашем коде. Чтобы сделать это, мы сначала узнаем об исходном способе обеспечения правильной обработки асинхронного кода, используя цикл событий: функции обратного вызова.
Функции обратного вызова
В примере setTimeout
функция с тайм-аутом запускалась после всего в основном контексте выполнения верхнего уровня. Но если вы хотите, чтобы одна из функций, например третья функция, выполнялась после тайм-аута, вам придется использовать методы асинхронного кодирования. Тайм-аут здесь может представлять собой асинхронный вызов API, который содержит данные. Вы хотите работать с данными из вызова API, но сначала убедитесь, что данные возвращаются.
Первоначальное решение этой проблемы — использование функций обратного вызова (callback functions). Функции обратного вызова не имеют специального синтаксиса; это просто функция, переданная в качестве аргумента другой функции. Функция, которая принимает другую функцию в качестве аргумента, называется функцией высшего порядка. Согласно этому определению, любая функция может стать функцией обратного вызова, если она передана в качестве аргумента. Обратные вызовы не являются асинхронными по своей природе, но могут использоваться для асинхронных целей.
Вот пример синтаксического кода функции высшего порядка и обратного вызова:
// Функция function fn() { console.log('Just a function') } // Функция, которая принимает другую функцию в качестве аргумента function higherOrderFunction(callback) { //Когда вы вызываете функцию, которая передается в качестве аргумента, она называется обратным вызовом. callback() } // Передача функции higherOrderFunction(fn)
В этом коде вы определяете функцию fn
, определяете функцию upperOrderFunction
, которая принимает обратный вызов функции в качестве аргумента, и передаете fn
в качестве обратного вызова в upperOrderFunction
.
Выполнение этого кода даст следующее:
Output Just a function
Вернемся к первой, второй и третьей функциям с setTimeout
. Вот что у вас есть на данный момент:
function first() { console.log(1) } function second() { setTimeout(() => { console.log(2) }, 0) } function third() { console.log(3) }
Задача состоит в том, чтобы third
функция всегда откладывала выполнение до завершения асинхронного действия в second
функции. Вот здесь и появляются обратные вызовы. Вместо выполнения first
, second
и third
на верхнем уровне выполнения вы передадите third
функцию в качестве аргумента second
. second
функция выполнит обратный вызов после завершения асинхронного действия.
Вот три функции с примененным обратным вызовом:
// Определите три функции function first() { console.log(1) } function second(callback) { setTimeout(() => { console.log(2) // Выполнить функцию обратного вызова callback() }, 0) } function third() { console.log(3) }
Теперь выполняется first
и second
, затем передается third
в качестве аргумента second
:
first() second(third)
После запуска этого блока кода вы получите следующий вывод:
Output 1 2 3
Сначала будет напечатано 1
, а после того, как таймер завершится (в данном случае ноль секунд, но вы можете изменить его на любое значение), он напечатает 2
, затем 3
. Передав функцию в качестве обратного вызова, вы успешно отложили выполнение до завершения асинхронного веб-API (setTimeout
).
Ключевой вывод здесь заключается в том, что функции обратного вызова не являются асинхронными — setTimeout
— это асинхронный веб-API, отвечающий за обработку асинхронных задач. Обратный вызов просто позволяет вам быть проинформированным о завершении асинхронной задачи и обрабатывает успех или неудачу задачи.
Теперь, когда мы узнали, как использовать обратные вызовы для обработки асинхронных задач, разберемся с проблемами вложенности слишком большого количества обратных вызовов и создания «пирамиды гибели».
Вложенные обратные вызовы и Callback Hell, также известный как Pyramid of Doom
Callback Hell — жаргонный термин, используемый для описания громоздкого количества вложенных операторов «if» или функций.
Функции обратного вызова — это эффективный способ обеспечить отложенное выполнение функции до тех пор, пока другая функция не завершится и не вернется с данными. Однако из-за вложенности обратных вызовов код может в конечном итоге запутаться, если у вас есть много последовательных асинхронных запросов, которые зависят друг от друга. Поначалу это было большим разочарованием для разработчиков JavaScript, и в результате код, содержащий вложенные обратные вызовы, часто называют «пирамидой гибели» или «адом обратных вызовов».
Вот демонстрация вложенных обратных вызовов:
function pyramidOfDoom() { setTimeout(() => { console.log(1) setTimeout(() => { console.log(2) setTimeout(() => { console.log(3) }, 500) }, 2000) }, 1000) }
В этом коде каждый новый setTimeout
вложен в функцию более высокого порядка, создавая пирамиду из более глубоких обратных вызовов. Запуск этого кода даст следующее:
Output 1 2 3
На практике в реальном асинхронном коде это может быть намного сложнее. Скорее всего, вам потребуется выполнить обработку ошибок в асинхронном коде, а затем передать некоторые данные из каждого ответа в следующий запрос. Выполнение этого с помощью обратных вызовов затруднит чтение и сопровождение вашего кода.
Вот работающий пример более реалистичной «пирамиды гибели»:
// Пример асинхронной функции function asynchronousRequest(args, callback) { // Выдает ошибку, если аргументы не переданы if (!args) { return callback(new Error('Whoa! Something went wrong.')) } else { return setTimeout( // Просто добавляем случайное число, чтобы казалось, что надуманная асинхронная функция вернула разные данные () => callback(null, {body: args + ' ' + Math.floor(Math.random() * 10)}), 500, ) } } // Вложенные асинхронные запросы function callbackHell() { asynchronousRequest('First', function first(error, response) { if (error) { console.log(error) return } console.log(response.body) asynchronousRequest('Second', function second(error, response) { if (error) { console.log(error) return } console.log(response.body) asynchronousRequest(null, function third(error, response) { if (error) { console.log(error) return } console.log(response.body) }) }) }) } // Выполнить callbackHell()
В этом коде вы должны сделать так, чтобы каждая функция учитывала возможный ответ и возможную ошибку, что делает функцию callbackHell
визуально запутанной.
Запуск этого кода даст вам следующее:
Output First 9 Second 3 Error: Whoa! Something went wrong. at asynchronousRequest (<anonymous>:4:21) at second (<anonymous>:29:7) at <anonymous>:9:13
Этому способу обработки асинхронного кода трудно следовать. В результате в ES6 была введена концепция промисов ( promises) — обещаний.
Промисы (Promises) — «обещания»
Промис представляет собой завершение асинхронной функции. Это объект, который может вернуть значение в будущем. Он выполняет ту же основную задачу, что и функция обратного вызова, но с множеством дополнительных функций и более читаемым синтаксисом. Как разработчик JavaScript вы, скорее всего, потратите больше времени на обещания, чем на их создание, поскольку обычно именно асинхронные веб-API возвращают обещание для использования разработчиком.
Создание промиса
Вы можете инициализировать промис с помощью синтаксиса new Promise
, и вы должны инициализировать его с помощью функции. Функция, которая передается в промис, имеет параметры разрешения (resolve
) и отклонения (reject
). Функции resolve
и reject
обрабатывают успех и сбой операции соответственно.
Напишите следующую строку, чтобы объявить промис:
// Инициализировать promise const promise = new Promise((resolve, reject) => {})
Если вы проверите инициализированное обещание в этом состоянии с помощью консоли своего веб-браузера, вы обнаружите, что оно имеет статус ожидания (pending
) и неопределенное (undefined
) значение:
Output __proto__: Promise [[PromiseStatus]]: "pending" [[PromiseValue]]: undefined
Пока для промися ничего не настроено, поэтому он будет оставаться в состоянии ожидания навсегда. Первое, что вы можете сделать, чтобы проверить промис, — это выполнить промис, разрешив его с помощью значения:
const promise = new Promise((resolve, reject) => { resolve('We did it!') })
Теперь, проверив промис, вы обнаружите, что он имеет статус «выполнено» ( fulfilled
) и значение, равное значению, которое вы передали для разрешения resolve
:
Output __proto__: Promise [[PromiseStatus]]: "fulfilled" [[PromiseValue]]: "We did it!"
Как мы знаем, промис — это объект, который может возвращать значение. После успешного выполнения значение переходит от неопределенного undefined
к заполнению данными.
Промис может иметь три возможных состояния: ожидание (pending), выполнено (fulfilled) и отклонено (rejected).
-
- Pending — исходное состояние до разрешения или отклонения.
- Fulfilled — операция выполнена успешно, промис выполнен.
- Rejected — операция не выполнена, промис отклонен.
После выполнения или отклонения промис считается выполненным.
Давайте посмотрим, как разработчик может использовать эти промисы.
Использование промисов
Промис в последнем примере выполнен со значением, но вы также хотите иметь доступ к значению. У промисов есть метод, называемый then
, который будет запускаться после того, как промис достигнет разрешения в коде. then
вернет значение обещания в качестве параметра.
Вот как бы вы вернули и вывели в консоль значение нашего примера промиса:
promise.then((response) => { console.log(response) })
Созданное вами обещание имело значение [[PromiseValue]]
We did it!
Это значение будет передано анонимной функции в качестве response
:
Output We did it!
До сих пор созданный вами пример не включал асинхронный веб-API — он только объяснял, как создавать, обрабатывать и использовать встроенное обещание JavaScript. Используя setTimeout
, вы можете протестировать асинхронный запрос.
Следующий код имитирует данные, возвращаемые из асинхронного запроса, как промис:
const promise = new Promise((resolve, reject) => { setTimeout(() => resolve('Resolving an asynchronous request!'), 2000) }) // Вывод результата promise.then((response) => { console.log(response) })
Использование синтаксиса then
гарантирует, что ответ response
будет зарегистрирован только после завершения операции setTimeout
через 2000
миллисекунд. Все это делается без вложенных обратных вызовов.
Промисы также могут быть связаны для передачи данных более чем одной асинхронной операции. Если значение возвращается в then
, может быть добавлено другое значение then
, которое будет соответствовать возвращаемому значению предыдущего then
:
// Привязать promise promise .then((firstResponse) => { // Вернуть новое значение для следующего then return firstResponse + ' And chaining!' }) .then((secondResponse) => { console.log(secondResponse) })
Выполненный ответ во втором then
выведет возвращаемое значение:
Output Resolving an asynchronous request! And chaining!
Поскольку then
можно объединить в цепочку, это позволяет использовать обещания более синхронно, чем обратные вызовы, поскольку они не нуждаются в вложении. Это позволит получить более читаемый код, который будет проще поддерживать и проверять.
Обработка ошибок
Пока мы обработали промисы только с успешным resolve
, которое переводит промис в состояние выполнения fulfilled
. Но часто с асинхронным запросом вам также приходится обрабатывать ошибку — если API не работает, или отправляется неверный или неавторизованный запрос. Обещание должно обрабатывать оба случая. В этом разделе мы создадим функцию для проверки успешности и ошибки при создании и использовании промиса.
Эта функция getUsers
передает флаг промису и возвращает промис:
function getUsers(onSuccess) { return new Promise((resolve, reject) => { setTimeout(() => { // Обработка resolve and reject в асинхронном API }, 1000) }) }
Изменим код так, чтобы, если onSuccess
истинно (true
), таймаут выполнялся с некоторыми данными. Если false, функция будет отклонена reject
с ошибкой:
function getUsers(onSuccess) { return new Promise((resolve, reject) => { setTimeout(() => { // Обработка resolve and reject в асинхронном API if (onSuccess) { resolve([ {id: 1, name: 'Jerry'}, {id: 2, name: 'Elaine'}, {id: 3, name: 'George'}, ]) } else { reject('Failed to fetch data!') } }, 1000) }) }
Для успешного результата вы возвращаете объекты JavaScript, которые представляют образцы пользовательских данных.
Чтобы обработать ошибку, вы воспользуетесь методом экземпляра catch
. Это даст вам обратный вызов ошибки с ошибкой error
в качестве параметра.
Запустите команду getUser
с параметром onSuccess
, установленным в false
, используя метод then
для случая успеха и метод catch
для ошибки:
// Запустите функцию getUsers с флагом false, чтобы вызвать ошибку getUsers(false) .then((response) => { console.log(response) }) .catch((error) => { console.error(error) })
Поскольку ошибка была вызвана, then
будет пропущен, и catch
обработает ошибку:
Output Failed to fetch data!
Если вместо этого переключить флаг на resolve
, catch
будет проигнорирован, и тепекрь будут возвращены данные:
// Запустите функцию getUsers с флагом true для успешного разрешения getUsers(true) .then((response) => { console.log(response) }) .catch((error) => { console.error(error) })
Это выведет данные пользователя:
Output (3) [{…}, {…}, {…}] 0: {id: 1, name: "Jerry"} 1: {id: 2, name: "Elaine"} 3: {id: 3, name: "George"}
Для справки, вот таблица с методами обработчика для объектов Promise:
Метод | Описание |
---|---|
then() |
Обрабатывает resolve . Возврашает promise, и асинхронно вызывает функцию onFulfilled |
catch() |
Обрабатывает reject . Возврашает promise, и асинхронно вызывает функцию onRejected |
finally() |
Вызывается, когда промис выполнен. Возврашает promise, и асинхронно вызывает функцию onFinally |
Промисы могут сбивать с толку как новых разработчиков, так и опытных программистов, которые никогда раньше не работали в асинхронной среде. Однако, как уже упоминалось, гораздо чаще используются готовые промисы, чем создаются свои. Обычно веб-API браузера или какая-либо сторонняя библиотека предоставляют промисы, и вам нужно только его использовать.
Далее рассмотрим типичный вариант использования веб-API, возвращающего обещания: Fetch API.
Использование Fetch API с промисами
Одним из наиболее полезных и часто используемых веб-API, возвращающих обещание, является Fetch API, который позволяет выполнять асинхронные запросы ресурсов по сети. fetch
представляет собой двухэтапный процесс, поэтому требует объединения then
. В этом примере демонстрируется промис к API GitHub для получения данных пользователя, а также обработка любой потенциальной ошибки:
// Получить пользователя из GitHub API //Если у Вас есть аккаунт GitHub, введите вместо "octocat" свой UserName fetch('https://api.github.com/users/octocat') .then((response) => { return response.json() }) .then((data) => { console.log(data) }) .catch((error) => { console.error(error) })
На URL https://api.github.com/users/octocat отправляется запрос fetch
, который асинхронно ожидает ответа. Первый then
передает ответ анонимной функции, которая форматирует ответ как данные JSON, а затем передает JSON второму then
, который записывает данные в консоль. Оператор catch
записывает любую ошибку в консоль.
Выполнение этого кода даст следующее:
Output login: "octocat", id: 583231, avatar_url: "https://avatars3.githubusercontent.com/u/583231?v=4" blog: "https://github.blog" company: "@github" followers: 3203 ...
Это данные, запрошенные с https://api.github.com/users/…, представленные в формате JSON.
Вывод: Промисы включают в себя множество улучшений для работы с асинхронным кодом. Но, хотя использовать then
для обработки асинхронных действий легче, чем пирамиду обратных вызовов, некоторые разработчики по-прежнему предпочитают синхронный формат написания асинхронного кода. Чтобы удовлетворить эту потребность, ECMAScript 2016 (ES7) представил асинхронные async
функции и ключевое слово await
, чтобы упростить работу с промисами.
Асинхронные функции с async/await
Функция async
позволяет обрабатывать асинхронный код так, чтобы он выглядел синхронным. async
функции также используют промисы под капотом, но имеют более традиционный синтаксис JavaScript. Рассмотрим примеры этого синтаксиса.
Вы можете создать async
функцию, добавив ключевое слово async
перед функцией:
// Создать async функцию async function getUser() { return {} }
Хотя эта функция еще не обрабатывает ничего асинхронного, она ведет себя иначе, чем традиционная функция. Но! Если вы выполните функцию, вы обнаружите, что она возвращает обещание с [[PromiseStatus]]
и [[PromiseValue]]
вместо возвращаемого значения.
Попробуйте вывод в консоль, вызвав функцию getUser
:
console.log(getUser())
Вы увидите следующее:
Output __proto__: Promise [[PromiseStatus]]: "fulfilled" [[PromiseValue]]: Object
Это означает, что вы можете обрабатывать асинхронную async
функцию с помощью then
так же, как и промис. Протестируйте это с помощью следующего кода:
getUser().then((response) => console.log(response))
Этот вызов getUser
передает возвращаемое значение анонимной функции, которая записывает значение в консоль.
При запуске этой программы вы получите следующее:
Output {}
Асинхронная async
функция может обрабатывать промис, вызываемый в ней, с помощью оператора ожидания await
. Этот оператор await
может использоваться в асинхронной async
функции и будет ждать, пока промис не разрешится, перед выполнением назначенного кода.
Теперь мы можем переписать запрос Fetch из последнего раздела с помощью async/await следующим образом:
// Обработка fetch при помощи async/await async function getUser() { const response = await fetch('https://api.github.com/users/octocat') const data = await response.json() console.log(data) } // Выполнение async функции getUser()
Здесь операторы await
гарантируют, что данные не будут выводиться в консоль, пока запрос не заполнит их данными.
Теперь окончательные данные data
можно обрабатывать внутри функции getUser
без необходимости использования then
:
Output login: "octocat", id: 583231, avatar_url: "https://avatars3.githubusercontent.com/u/583231?v=4" blog: "https://github.blog" company: "@github" followers: 3203 ...
Примечание. Во многих средах асинхронный режим async
необходим для использования await
, однако некоторые новые версии браузеров и Node позволяют использовать await
верхнего уровня, что позволяет обойтись без создания асинхронной функции для обертывания await
.
Наконец, поскольку мы обрабатываем выполненное обещание в асинхронной функции, вы также можете обрабатывать ошибку внутри функции. Вместо использования метода catch
с then
, для обработки исключения вы будете использовать шаблон try
/catch
.
// Обработка success и errors при помощи async/await async function getUser() { try { // Обработка success const response = await fetch('https://api.github.com/users/octocat') const data = await response.json() console.log(data) } catch (error) { // Обработка ошибки в catch console.error(error) } }
Программа теперь перейдет к блоку catch
, если получит ошибку, и запишет эту ошибку в консоль.
Современный асинхронный код JavaScript чаще всего обрабатывается с синтаксисом async
/await
, но важно иметь практические знания о том, как работают промисы, тем более что промисы могут иметь дополнительные функции, которые не могут быть обработаны с помощью async
/await
, например, объединение обещаний с Promise.all()
.
Заключение
Поскольку веб-API часто предоставляют данные асинхронно, изучение того, как обрабатывать результат асинхронных действий, является важной частью работы разработчика JavaScript. Мы рассмотрели, как среда хоста использует цикл событий для обработки порядка выполнения кода со стеком и очередью. Мы также опробовали примеры трех способов обработки успеха или отказа асинхронного события с помощью обратных вызовов, промисов и синтаксиса async
/await
. Наконец, мы использовали Fetch Web API для обработки асинхронных действий.
Читайте больше по теме: