Основное об Async/await

async - это ключевое слово, которое ставится перед функцией и указывает, что функция является асинхронной. Функция, помеченная как async, всегда возвращает промис. Если функция возвращает значение, то оно будет обернуто в промис. Если функция выбрасывает ошибку, то промис будет отклонен с этой ошибкой.

async function getData() {
  const response = await fetch("https://example.com/data")
  const data = await response.json()
  return data
}

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

async function getData() {
  const response = await fetch("https://example.com/data")
  const data = await response.json()
  return data
}

В данном примере, await используется для ожидания выполнения асинхронной операции получения данных с сервера. Когда операция завершается, await возвращает результат выполнения операции.

Использование async и await позволяет писать код, который выглядит как синхронный, но при этом выполняется асинхронно. Это делает код более понятным и удобным для чтения и отладки. Кроме того, async и await упрощают обработку ошибок в асинхронном коде, так как ошибки могут быть обработаны с помощью конструкции try-catch.

Async/await под капотом

Async/await является синтаксическим сахаром взаимодействия промиса с генераторами.

function* main() {
  console.log('Entry');

  const result = yield wait();
  console.log(result);

  const result2 = yield wait();
  console.log(result2);

  // ...

  console.log('Exit');
  return 'Return';
}

run(main).then(result => {
  console.log(result);
});

function run(fn, ...args) {
  const it = fn(...args);

  return new Promise((resolve, reject) => {
    function step(resolvedValue) {
      const result = it.next(resolvedValue);

      // Условие выхода
      if (result.done) {
        resolve(result.value);
        return;
      }

      result.value.then(resolvedValue => {
        step(resolvedValue);
      });
    }

Почему использовать return await плохая идея?

Правило no-return-await в ESLint запрещает использование return await в асинхронной функции. Потому что: возвращаемое значение асинхронной функции всегда оборачивается в Promise.resolve , то return await на самом деле ничего не делает, кроме как добавляет дополнительное время, прежде чем Promise закончится resolve или reject .

Верхнеуровневый await и асинхронные модули

Верхнеуровневый await (top level await) позволяет использовать этот оператор за пределами асинхронных функций:

const connection = await dbConnector()
const jQuery = await import("http://cdn.com/jquery")

_Но его можно использовать только либо внутри ES6-модулей, либо в DevTools. Такое ограничение связано с тем, что await — это синтаксический сахар, который работает через модули. _

Например, модуль с верхнеуровневым await для разработчика выглядит так:

// module.mjs
const value = await Promise.resolve("^_^")
 
export { value }
 
// main.mjs
import { value } from "./module.mjs"
 
console.log(value) // ^_^

Ни внутри модуля, ни внутри основной программы нет асинхронных функций, но при этом они необходимы для работы await. Как же тогда работает этот код?

Дело в том, что движок сам возьмёт на себя задачу обернуть await в асинхронную функцию, поэтому где-то «под капотом», без синтаксического сахара, верхнеуровневый await будет выглядеть примерно так:

// module.mjs
export let value
export const promise = (async () => {
  value = await Promise.resolve("^_^")
})()
 
export { value, promise }
 
// main.mjs
import { value, promise } from "./module.mjs"
 
;(async () => {
  await promise
  console.log(value) // ^_^
})()

Никакой магии. Просто синтаксический сахар.


Назад