Spec-Zone.ru › JavaScript

async function

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

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

Объявление async function создает привязку новой асинхронной функции к заданному имени. Ключевое слово await разрешено внутри тела функции, позволяя писать асинхронное, основанное на промисах поведение в более чистом стиле и избегая необходимости явной настройки цепочек промисов.

Вы также можете определить асинхронные функции, используя async function выражение.

Попробуйте

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

async function asyncCall() {
  console.log("calling");
  const result = await resolveAfter2Seconds();
  console.log(result);
  // Expected output: "resolved"
}

asyncCall();

Синтаксис

async function name(param0) {
  statements
}
async function name(param0, param1) {
  statements
}
async function name(param0, param1, /* …, */ paramN) {
  statements
}

Примечание: Между async и function не может быть разрыва строки, иначе будет автоматически вставлена точка с запятой, что приведет к тому, что async станет идентификатором, а остальная часть — объявлением function.

Параметры

name
Имя функции.
param Необязательный
Имя формального параметра функции. Синтаксис параметров см. в справочнике по функциям.
statements Необязательный
Инструкции, составляющие тело функции. Может использоваться механизм await.

Описание

Объявление async function создает объект AsyncFunction. Каждый раз, когда вызывается асинхронная функция, она возвращает новый Promise, который будет разрешен значением, возвращаемым асинхронной функцией, или отклонен исключением, не перехваченным внутри асинхронной функции.

Асинхронные функции могут содержать ноль или более выражений await. Выражения await делают функции, возвращающие промисы, ведущими себя как синхронные, приостанавливая выполнение до тех пор, пока возвращенный промис не будет выполнен или отклонен. Разрешенное значение промиса рассматривается как возвращаемое значение выражения await. Использование async и await позволяет использовать обычные блоки try / catch вокруг асинхронного кода.

Примечание: Ключевое слово await допустимо только внутри асинхронных функций в обычном коде JavaScript. Если вы используете его вне тела асинхронной функции, вы получите SyntaxError.

await можно использовать отдельно с модулями JavaScript.

Примечание: Назначение async/await — упростить синтаксис, необходимый для использования API, основанных на промисах. Поведение async/await похоже на объединение генераторов и промисов.

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

Например, рассмотрим следующий код:

async function foo() {
  return 1;
}

Это похоже на:

function foo() {
  return Promise.resolve(1);
}

Обратите внимание, что, хотя возвращаемое значение асинхронной функции ведет себя так, как будто оно обернуто в Promise.resolve, они не эквивалентны. Асинхронная функция возвращает другую *ссылку*, в то время как Promise.resolve возвращает ту же ссылку, если данное значение является промисом. Это может стать проблемой, когда вы хотите проверить равенство промиса и возвращаемого значения асинхронной функции.

const p = new Promise((res, rej) => {
  res(1);
});

async function asyncReturn() {
  return p;
}

function basicReturn() {
  return Promise.resolve(p);
}

console.log(p === basicReturn()); // true
console.log(p === asyncReturn()); // false

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

Например:

async function foo() {
  await 1;
}

Это также эквивалентно:

function foo() {
  return Promise.resolve(1).then(() => undefined);
}

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

В следующем примере мы последовательно ожидаем два промиса. Прогресс проходит через функцию foo в три этапа.

  1. Первая строка тела функции foo выполняется синхронно, при этом выражение await конфигурируется с ожидающим промисом. Затем прогресс через foo приостанавливается, и управление передается обратно функции, вызвавшей foo.
  2. Некоторое время спустя, когда первый промис либо выполнен, либо отклонен, управление возвращается в foo. Результат выполнения первого промиса (если он не был отклонен) возвращается из выражения await. Здесь 1 присваивается result1. Прогресс продолжается, и оценивается второе выражение await. Снова прогресс через foo приостанавливается, и управление передается.
  3. Некоторое время спустя, когда второй промис либо выполнен, либо отклонен, управление снова входит в foo. Результат разрешения второго промиса возвращается из второго выражения await. Здесь 2 присваивается result2. Управление переходит к выражению return (если оно есть). Значение по умолчанию undefined возвращается как разрешенное значение текущего промиса.
async function foo() {
  const result1 = await new Promise((resolve) =>
    setTimeout(() => resolve("1")),
  );
  const result2 = await new Promise((resolve) =>
    setTimeout(() => resolve("2")),
  );
}
foo();

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

Например, в следующем коде будет сгенерирована ошибка необработанного отклонения промиса, даже если обработчик .catch был настроен далее по цепочке промисов. Это связано с тем, что p2 не будет "подключен" к цепочке промисов до тех пор, пока управление не вернется из p1.

async function foo() {
  const p1 = new Promise((resolve) => setTimeout(() => resolve("1"), 1000));
  const p2 = new Promise((_, reject) =>
    setTimeout(() => reject(new Error("failed")), 500),
  );
  const results = [await p1, await p2]; // Do not do this! Use Promise.all or Promise.allSettled instead.
}
foo().catch(() => {}); // Attempt to swallow all errors...

Объявления async function ведут себя подобно объявлениям function — они поднимаются в начало своей области видимости и могут быть вызваны в любом месте своей области видимости, и их можно переопределить только в определенных контекстах.

Примеры

Асинхронные функции и порядок выполнения

function resolveAfter2Seconds() {
  console.log("starting slow promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("slow");
      console.log("slow promise is done");
    }, 2000);
  });
}

function resolveAfter1Second() {
  console.log("starting fast promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("fast");
      console.log("fast promise is done");
    }, 1000);
  });
}

async function sequentialStart() {
  console.log("== sequentialStart starts ==");

  // 1. Start a timer, log after it's done
  const slow = resolveAfter2Seconds();
  console.log(await slow);

  // 2. Start the next timer after waiting for the previous one
  const fast = resolveAfter1Second();
  console.log(await fast);

  console.log("== sequentialStart done ==");
}

async function sequentialWait() {
  console.log("== sequentialWait starts ==");

  // 1. Start two timers without waiting for each other
  const slow = resolveAfter2Seconds();
  const fast = resolveAfter1Second();

  // 2. Wait for the slow timer to complete, and then log the result
  console.log(await slow);
  // 3. Wait for the fast timer to complete, and then log the result
  console.log(await fast);

  console.log("== sequentialWait done ==");
}

async function concurrent1() {
  console.log("== concurrent1 starts ==");

  // 1. Start two timers concurrently and wait for both to complete
  const results = await Promise.all([
    resolveAfter2Seconds(),
    resolveAfter1Second(),
  ]);
  // 2. Log the results together
  console.log(results[0]);
  console.log(results[1]);

  console.log("== concurrent1 done ==");
}

async function concurrent2() {
  console.log("== concurrent2 starts ==");

  // 1. Start two timers concurrently, log immediately after each one is done
  await Promise.all([
    (async () => console.log(await resolveAfter2Seconds()))(),
    (async () => console.log(await resolveAfter1Second()))(),
  ]);
  console.log("== concurrent2 done ==");
}

sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"

// wait above to finish
setTimeout(sequentialWait, 4000); // after 2 seconds, logs "slow" and then "fast"

// wait again
setTimeout(concurrent1, 7000); // same as sequentialWait

// wait again
setTimeout(concurrent2, 10000); // after 1 second, logs "fast", then after 1 more second, "slow"

await и конкурентность

В sequentialStart выполнение приостанавливается на 2 секунды для первого await, а затем еще на секунду для второго await. Второй таймер не создается до тех пор, пока первый не сработает, поэтому код завершается через 3 секунды.

В sequentialWait оба таймера создаются, а затем await. Таймеры выполняются параллельно, что означает, что код завершается за 2, а не за 3 секунды, т.е. за время самого медленного таймера. Однако вызовы await все равно выполняются последовательно, что означает, что второе await будет ждать завершения первого. В этом случае результат самого быстрого таймера обрабатывается после самого медленного.

Если вы хотите безопасно выполнять другие задачи после завершения двух или более задач, выполняющихся параллельно, вам необходимо дождаться вызова Promise.all() или Promise.allSettled() перед этой задачей.

Предупреждение: Функции sequentialWait и concurrent1 функционально не эквивалентны.

В sequentialWait, если промис fast отклоняется до того, как промис slow будет выполнен, будет сгенерирована ошибка необработанного отклонения промиса, независимо от того, настроил ли вызывающий код блок catch.

В concurrent1 Promise.all собирает цепочку промисов за один раз, что означает, что операция завершится быстро, независимо от порядка отклонения промисов, и ошибка всегда произойдет в пределах настроенной цепочки промисов, что позволит перехватить ее обычным способом.

Переписывание цепочки промисов с помощью асинхронной функции

API, возвращающий Promise, приведет к созданию цепочки промисов и разделит функцию на множество частей. Рассмотрим следующий код:

function getProcessedData(url) {
  return downloadData(url) // returns a promise
    .catch((e) => downloadFallbackData(url)) // returns a promise
    .then((v) => processDataInWorker(v)); // returns a promise
}

Его можно переписать с помощью одной асинхронной функции следующим образом:

async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url);
  } catch (e) {
    v = await downloadFallbackData(url);
  }
  return processDataInWorker(v);
}

Альтернативно, вы можете связать промис с помощью catch():

async function getProcessedData(url) {
  const v = await downloadData(url).catch((e) => downloadFallbackData(url));
  return processDataInWorker(v);
}

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

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

Спецификация
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
async_function
55
15
52
42
10.1
55
52
42
10.3
6.0
55
10.3
1.0.0
1.0
7.6.0

См. также

  • Руководство по функциям
  • Руководство по использованию промисов
  • Функции
  • AsyncFunction
  • async function выражение
  • function
  • function*
  • async function*
  • await
  • Promise
  • Декорирование асинхронных JavaScript-функций на innolitics.com (2016)

© 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/Statements/async_function

Spec-Zone.ru

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