Spec-Zone.ru › JavaScript

await using

Базовая доступность Ограничена

Эта возможность не является базовой, поскольку она не работает в некоторых из наиболее широко используемых браузеров.

Объявление await using объявляет локальные переменные с областью видимости блока, которые асинхронно высвобождаются (disposed). Как и const, переменные, объявленные с помощью await using, должны быть инициализированы и не могут быть переназначены. Значение переменной должно быть либо null, либо undefined, либо объектом с методом [Symbol.asyncDispose]() или [Symbol.dispose](). Когда переменная выходит из области видимости, вызывается и ожидается метод [Symbol.asyncDispose]() или [Symbol.dispose]() объекта, чтобы гарантировать высвобождение ресурсов.

Синтаксис

await using name1 = value1;
await using name1 = value1, name2 = value2;
await using name1 = value1, name2 = value2, /* …, */ nameN = valueN;
nameN
Имя объявляемой переменной. Каждое должно быть допустимым идентификатором JavaScript и не быть шаблоном привязки деструктурирования.
valueN
Начальное значение переменной. Это может быть любое допустимое выражение, но его значение должно быть либо null, либо undefined, либо объектом с методом [Symbol.asyncDispose]() или [Symbol.dispose]().

Описание

Это объявление может использоваться только там, где могут использоваться как await, так и using, включая:

  • Внутри блока (если блок также находится в асинхронном контексте)
  • Внутри тела асинхронной функции или асинхронной функции-генератора
  • На верхнем уровне модуля
  • В инициализаторе цикла for, for...of (если цикл for также находится в асинхронном контексте) или for await...of

await using объявляет асинхронный высвобождаемый ресурс, привязанный к времени жизни области видимости переменной (блок, функция, модуль и т. д.). При выходе из области видимости ресурс высвобождается асинхронно. Его синтаксис может быть несколько запутанным, потому что await не оказывает эффекта ожидания (awaiting effect) при первом объявлении переменной, а только когда переменная выходит из области видимости.

Когда переменная впервые объявляется и ее значение не является null-подобным, из объекта извлекается высвободитель (disposer). Сначала проверяется свойство [Symbol.asyncDispose], а затем используется [Symbol.dispose], если [Symbol.asyncDispose] равно undefined. Если ни одно из свойств не содержит функции, выбрасывается TypeError. Примечательно, что метод [Symbol.dispose]() оборачивается в функцию, которая выглядит как async () => { object[Symbol.dispose](); }, что означает, что если он возвращает промис, этот промис не ожидается (awaited). Этот высвободитель сохраняется в области видимости.

Когда переменная выходит из области видимости, высвободитель вызывается и ожидается. Если область видимости содержит несколько объявлений using или await using, все высвободители выполняются последовательно в обратном порядке объявления, независимо от типа объявления. Гарантируется выполнение всех высвободителей (во многом как блок finally в try...catch...finally). Все ошибки, возникающие во время высвобождения, включая первоначальную ошибку, вызвавшую выход из области видимости (если применимо), агрегируются внутри одного SuppressedError, причем каждое более раннее исключение выступает в качестве свойства suppressed, а более позднее исключение — в качестве свойства error. Этот SuppressedError выбрасывается после завершения высвобождения.

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

await using привязывает управление ресурсами к лексическим областям видимости, что удобно, но иногда сбивает с толку. Ниже приведены примеры того, когда это может работать не так, как вы ожидаете. Если вы хотите управлять высвобождением ресурсов вручную, сохраняя при этом те же гарантии обработки ошибок, вы можете использовать AsyncDisposableStack.

Примеры

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

Основное использование

Обычно вы используете await using для ресурсов, предоставляемых библиотекой, которые уже реализуют протокол асинхронного высвобождения. Например, FileHandle в Node.js является асинхронно высвобождаемым:

import fs from "node:fs/promises";

async function example() {
  await using file = await fs.open("example.txt", "r");
  console.log(await file.read());
  // Before `file` goes out of scope, it is disposed by calling `file[Symbol.asyncDispose]()` and awaited.
}

Обратите внимание, что в объявлении file есть две операции await, которые делают разные вещи и обе необходимы. await fs.open() вызывает ожидание во время получения: он ожидает открытия файла и разворачивает возвращенный промис в объект FileHandle. await using file вызывает ожидание во время высвобождения: он обеспечивает асинхронное высвобождение file при выходе переменной из области видимости.

await using с for await...of

Очень легко спутать следующие три синтаксиса:

  • for await (using x of y) { ... }
  • for (await using x of y) { ... }
  • for (using x of await y) { ... }

Еще больше сбивает с толку то, что их можно использовать вместе.

for await (await using x of await y) {
  // ...
}

Во-первых, await y делает то, что вы ожидаете: мы await промис y, который, как ожидается, разрешится в объект, по которому мы итерируемся. Отложим этот вариант в сторону.

Цикл for await...of требует, чтобы объект y был асинхронно итерируемым (async iterable). Это означает, что объект должен иметь метод [Symbol.asyncIterator], который возвращает асинхронный итератор, чей метод next() возвращает промис, представляющий результат. Это используется в тех случаях, когда итерируемый объект не знает, каково следующее значение, или даже завершена ли итерация, пока не завершится какая-либо асинхронная операция.

С другой стороны, синтаксис await using x требует, чтобы объект x, полученный из итерируемого объекта, был асинхронно высвобождаемым (async disposable). Это означает, что объект должен иметь метод [Symbol.asyncDispose], который возвращает промис, представляющий операцию высвобождения. Это отдельная проблема от самой итерации, и она вызывается только тогда, когда переменная x выходит из области видимости.

Другими словами, все следующие четыре комбинации являются допустимыми и выполняют разные действия:

  • for (using x of y): y итерируется синхронно, выдавая по одному результату за раз, который можно высвободить синхронно.
  • for await (using x of y): y итерируется асинхронно, выдавая по одному результату за раз после ожидания, но значение результата можно высвободить синхронно.
  • for (await using x of y): y итерируется синхронно, выдавая по одному результату за раз, но значение результата можно высвободить только асинхронно.
  • for await (await using x of y): y итерируется асинхронно, выдавая по одному результату за раз после ожидания, и значение результата можно высвободить только асинхронно.

Ниже мы создадим несколько фиктивных значений y, чтобы продемонстрировать варианты их использования. Для асинхронных API мы основываем наш код на модуле Node.js fs/promises.

const syncIterableOfSyncDisposables = [
  stream1.getReader(),
  stream2.getReader(),
];
for (using reader of syncIterableOfSyncDisposables) {
  console.log(reader.read());
}
async function* requestMany(urls) {
  for (const url of urls) {
    const res = await fetch(url);
    yield res.body.getReader();
  }
}
const asyncIterableOfSyncDisposables = requestMany([
  "https://example.com",
  "https://example.org",
]);
for await (using reader of asyncIterableOfSyncDisposables) {
  console.log(reader.read());
}
const syncIterableOfAsyncDisposables = await Promise.all(
  fs.globSync("*.txt").map((path) => fs.open(path, "r")),
);
for (await using file of syncIterableOfAsyncDisposables) {
  console.log(await file.read());
}
async function* globHandles(pattern) {
  for await (const path of fs.glob(pattern)) {
    yield await fs.open(path, "r");
  }
}
const asyncIterableOfAsyncDisposables = globHandles("*.txt");
for await (await using file of asyncIterableOfAsyncDisposables) {
  console.log(await file.read());
}

Неявное ожидание при выходе из области видимости

Как только в области видимости объявляется одно await using, область видимости всегда будет иметь await при выходе, даже если переменная равна null или undefined. Это обеспечивает стабильный порядок выполнения и обработку ошибок. Примеры Эффектов await на поток управления содержат больше подробностей об этом.

В примере ниже вызов example() разрешается только через один такт, из-за неявного await при возврате функции.

async function example() {
  await using nothing = null;
  console.log("Example call");
}

example().then(() => console.log("Example done"));
Promise.resolve().then(() => console.log("Microtask done"));
// Output:
// Example call
// Microtask done
// Example done

Рассмотрим тот же код, но вместо него с синхронным using. На этот раз вызов example() разрешается немедленно, поэтому оба обработчика then() вызываются в том же такте.

async function example() {
  using nothing = null;
  console.log("Example call");
}

example().then(() => console.log("Example done"));
Promise.resolve().then(() => console.log("Microtask done"));
// Output:
// Example call
// Example done
// Microtask done

Для более реалистичного примера рассмотрим два одновременных вызова функции:

class Resource {
  #name;
  constructor(name) {
    this.#name = name;
  }
  async [Symbol.asyncDispose]() {
    console.log(`Disposing resource ${this.#name}`);
  }
}

async function example(id, createOptionalResource) {
  await using required = new Resource(`required ${id}`);
  await using optional = createOptionalResource
    ? new Resource("optional")
    : null;
  await using another = new Resource(`another ${id}`);
}

example(1, true);
example(2, false);
// Output:
// Disposing resource another 1
// Disposing resource another 2
// Disposing resource optional
// Disposing resource required 1
// Disposing resource required 2

Как видите, ресурс required 2 высвобождается в том же такте, что и required 1. Если бы ресурс optional не вызвал избыточного await, то required 2 был бы высвобожден раньше, что было бы одновременно с optional.

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

Спецификация
ECMAScript Async Explicit Resource Management
# sec-let-and-const-declarations

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

Десктоп Мобильные Сервер
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_using
134
134
141
119
preview
134
141
88
Нет
29.0
134
Нет
1.0.23
2.2.10
24.0.0

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

  • Управление ресурсами JavaScript
  • const
  • let
  • using
  • Symbol.asyncDispose
  • AsyncDisposableStack

© 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/await_using

Spec-Zone.ru

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