Spec-Zone.ru › JavaScript

await

Базовая доступность Широко доступно

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна во всех браузерах с апреля 2017 года.

Оператор await используется для ожидания Promise и получения его значения исполнения (fulfillment value). Он может использоваться только внутри асинхронной функции или на верхнем уровне модуля.

Синтаксис

await expression

Параметры

expression
Объект Promise, thenable-объект или любое значение, которое нужно дождаться.

Возвращаемое значение

Значение исполнения промиса или thenable-объекта, или, если выражение не является thenable-объектом, собственное значение выражения.

Исключения

Генерирует причину отклонения, если промис или thenable-объект был отклонён.

Описание

await обычно используется для развёртывания промисов путём передачи Promise в качестве expression. Использование await приостанавливает выполнение окружающих его async функций до тех пор, пока промис не будет завершён (то есть исполнен или отклонён). Когда выполнение возобновляется, значение выражения await становится значением исполненного промиса.

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

expression разрешается (resolves) так же, как и Promise.resolve(): оно всегда преобразуется в нативный Promise, а затем ожидается. Если expression является:

  • Нативным Promise (что означает, что expression принадлежит Promise или подклассу, и expression.constructor === Promise): Промис используется напрямую и ожидается нативно, без вызова then().
  • Thenable-объектом (включая ненативные промисы, полифиллы, прокси, дочерние классы и т. д.): Новый промис конструируется с использованием нативного конструктора Promise() путём вызова метода then() объекта и передачи обработчика, который вызывает колбэк resolve.
  • Не-thenable-значением: Конструируется и используется уже исполненный Promise.

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

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

Примеры

Ожидание исполнения промиса

Если Promise передаётся выражению await, оно ожидает исполнения Promise и возвращает исполненное значение.

function resolveAfter2Seconds(x) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  const x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}

f1();

Thenable-объекты

Thenable-объекты разрешаются так же, как и фактические объекты Promise.

async function f2() {
  const thenable = {
    then(resolve) {
      resolve("resolved!");
    },
  };
  console.log(await thenable); // "resolved!"
}

f2();

Они также могут быть отклонены:

async function f2() {
  const thenable = {
    then(_, reject) {
      reject(new Error("rejected!"));
    },
  };
  await thenable; // Throws Error: rejected!
}

f2();

Преобразование в промис

Если значение не является Promise, await преобразует значение в разрешённый Promise и ожидает его. Идентичность ожидаемого значения не меняется, если оно не имеет вызываемого свойства then.

async function f3() {
  const y = await 20;
  console.log(y); // 20

  const obj = {};
  console.log((await obj) === obj); // true
}

f3();

Обработка отклонённых промисов

Если Promise отклонён, генерируется отклонённое значение.

async function f4() {
  try {
    const z = await Promise.reject(new Error("rejected!"));
  } catch (e) {
    console.error(e); // Error: rejected!
  }
}

f4();

Вы можете обрабатывать отклонённые промисы без блока try, прикрепляя обработчик catch() перед ожиданием промиса.

const response = await promisedFunction().catch((err) => {
  console.error(err);
  return "default response";
});
// response will be "default response" if the promise is rejected

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

function promisedFunction() {
  // Immediately return a promise to minimize chance of an error being thrown
  return new Promise((resolve, reject) => {
    // do something async
  });
}

Однако, если promisedFunction() синхронно генерирует ошибку, эта ошибка не будет перехвачена обработчиком catch(). В этом случае оператор try...catch необходим.

await верхнего уровня

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

Вот пример модуля, использующего Fetch API и указывающего await в операторе export. Любые модули, которые импортируют его, будут ждать разрешения запроса (fetch), прежде чем выполнять какой-либо код.

// fetch request
const colors = fetch("../data/colors.json").then((response) => response.json());

export default await colors;

Эффекты await на поток управления

Когда в коде встречается await (либо в асинхронной функции, либо в модуле), ожидаемое выражение выполняется, в то время как весь код, зависящий от значения выражения, приостанавливается. Управление выходит из функции и возвращается вызывающей стороне. Когда значение ожидаемого выражения разрешается, планируется ещё одна микрозадача, которая продолжает приостановленный код. Это происходит, даже если ожидаемое значение — это уже разрешённый промис или вообще не промис: выполнение не возвращается к текущей функции, пока не будут обработаны все другие уже запланированные микрозадачи. Например, рассмотрим следующий код:

async function foo(name) {
  console.log(name, "start");
  console.log(name, "middle");
  console.log(name, "end");
}

foo("First");
foo("Second");

// First start
// First middle
// First end
// Second start
// Second middle
// Second end

В этом случае функция foo по сути синхронна, поскольку не содержит выражений await. Эти три оператора происходят в одном тике. Следовательно, два вызова функции выполняют все операторы последовательно. В терминах промисов функция соответствует:

function foo(name) {
  return new Promise((resolve) => {
    console.log(name, "start");
    console.log(name, "middle");
    console.log(name, "end");
    resolve();
  });
}

Однако, как только появляется хотя бы один await, функция становится асинхронной, и выполнение последующих операторов откладывается до следующего тика.

async function foo(name) {
  console.log(name, "start");
  await console.log(name, "middle");
  console.log(name, "end");
}

foo("First");
foo("Second");

// First start
// First middle
// Second start
// Second middle
// First end
// Second end

Это соответствует:

function foo(name) {
  return new Promise((resolve) => {
    console.log(name, "start");
    resolve(console.log(name, "middle"));
  }).then(() => {
    console.log(name, "end");
  });
}

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

Микрозадачи планируются не только при разрешении промисов, но и другими веб-API, и они выполняются с одинаковым приоритетом. В этом примере используется queueMicrotask(), чтобы продемонстрировать, как обрабатывается очередь микрозадач при встрече каждого выражения await.

let i = 0;

queueMicrotask(function test() {
  i++;
  console.log("microtask", i);
  if (i < 3) {
    queueMicrotask(test);
  }
});

(async () => {
  console.log("async function start");
  for (let i = 1; i < 3; i++) {
    await null;
    console.log("async function resume", i);
  }
  await null;
  console.log("async function end");
})();

queueMicrotask(() => {
  console.log("queueMicrotask() after calling async function");
});

console.log("script sync part end");

// Logs:
// async function start
// script sync part end
// microtask 1
// async function resume 1
// queueMicrotask() after calling async function
// microtask 2
// async function resume 2
// microtask 3
// async function end

В этом примере функция test() всегда вызывается до возобновления асинхронной функции, поэтому микрозадачи, которые они планируют, всегда выполняются взаимосвязанным образом. С другой стороны, поскольку и await, и queueMicrotask() планируют микрозадачи, порядок выполнения всегда основывается на порядке планирования. Вот почему запись "queueMicrotask() after calling async function" происходит после того, как асинхронная функция возобновляется в первый раз.

Улучшение трассировки стека

Иногда await опускается, когда промис напрямую возвращается из асинхронной функции.

async function noAwait() {
  // Some actions...

  return /* await */ lastAsyncTask();
}

Однако рассмотрим случай, когда lastAsyncTask асинхронно генерирует ошибку.

async function lastAsyncTask() {
  await null;
  throw new Error("failed");
}

async function noAwait() {
  return lastAsyncTask();
}

noAwait();

// Error: failed
//    at lastAsyncTask

Только lastAsyncTask появляется в трассировке стека, потому что промис отклоняется после того, как он уже был возвращён из noAwait — в некотором смысле промис не связан с noAwait. Чтобы улучшить трассировку стека, вы можете использовать await для развёртывания промиса, чтобы исключение было сгенерировано в текущей функции. Затем исключение будет немедленно завёрнуто в новый отклонённый промис, но во время создания ошибки вызывающая сторона появится в трассировке стека.

async function lastAsyncTask() {
  await null;
  throw new Error("failed");
}

async function withAwait() {
  return await lastAsyncTask();
}

withAwait();

// Error: failed
//    at lastAsyncTask
//    at async withAwait

Вопреки распространённому мнению, return await promise, по крайней мере, так же быстр, как return promise, благодаря тому, как спецификация и движки оптимизируют разрешение нативных промисов. Существует предложение сделать return promise быстрее, а также вы можете прочитать об оптимизации V8 для асинхронных функций. Следовательно, за исключением стилистических причин, return await почти всегда предпочтительнее.

Спецификации

Спецификация
ECMAScript® 2027 Language Specification
# sec-async-function-definitions

Совместимость с браузерами

Десктопные Мобильные Серверные
Chrome Edge Firefox Opera Safari Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet WebView Android WebView on iOS Bun Deno Node.js
await
55
14
52
42
10.1
55
52
42
10.3
6.0
55
10.3
1.0.0
1.0
7.6.0
top_level
89
89
89
75
27
15–27Не поддерживает одновременный импорт несколькими модулями модуля, содержащего await верхнего уровня (см. bug 242740).
89
89
63
27
15–27Не поддерживает одновременный импорт несколькими модулями модуля, содержащего await верхнего уровня (см. bug 242740).
15.0
89
27
15–27Не поддерживает одновременный импорт несколькими модулями модуля, содержащего await верхнего уровня (см. bug 242740).
1.0.0
1.0
14.8.0Не поддерживается в модулях CommonJS.

Смотрите также

  • async function
  • async function expression
  • AsyncFunction
  • await верхнего уровня on v8.dev (2019)
  • typescript-eslint rule: return-await

© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API