Цикл событий, обратных вызовов, промисов и Async/Await в JavaScript

5 (100%) 4 vote[s]

На заре Интернета веб-сайты часто состояли из статических данных на странице 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 функции. Вот здесь и появляются обратные вызовы. Вместо выполнения firstsecond и 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 для обработки асинхронных действий.

Читайте больше по теме:

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 235

Популярные записи