Start · Sprachen · JavaScript · Referenz · AsyncDisposableStack

AsyncDisposableStack

Klasse

Repräsentiert einen Stapel asynchroner Disposer-Funktionen, die in umgekehrter Reihenfolge ausgeführt werden, sobald der Stapel selbst verworfen (disposed) wird.

seit JavaScript ES2025 (Stage 4, experimentell – noch nicht in all Kategorie: core

Signatur

class AsyncDisposableStack

Beschreibung

AsyncDisposableStack ist Teil des Explicit Resource Management-Vorschlags (ECMAScript 2025) und ermöglicht die geordnete, asynchrone Freigabe von Ressourcen wie Datenbankverbindungen, Dateisystem-Handles oder Netzwerk-Sockets. Der Stapel funktioniert nach dem LIFO-Prinzip (Last In, First Out): Ressourcen werden in umgekehrter Reihenfolge ihrer Registrierung freigegeben.

Ressourcen können mit use(), adopt() oder defer() auf dem Stapel registriert werden. Beim Aufruf von disposeAsync() – oder beim Verlassen eines await using-Blocks – werden alle registrierten Disposer der Reihe nach asynchron aufgerufen. Fehler werden gesammelt und als SuppressedError gemeldet, sodass kein Disposer übersprungen wird.

AsyncDisposableStack implementiert das Symbol.asyncDispose-Protokoll und kann daher direkt mit der await using-Syntax in Verbindung mit dem expliziten Ressourcen-Management genutzt werden. Im Gegensatz zu DisposableStack unterstützt er asynchrone Disposer-Funktionen (d. h. Funktionen, die ein Promise zurückgeben).

  • Verwende use() für Objekte, die bereits Symbol.asyncDispose implementieren.
  • Verwende adopt() für Ressourcen, die kein Dispose-Protokoll kennen (z. B. rohe Handles).
  • Verwende defer() für beliebige asynchrone Aufräum-Callbacks ohne zugehörige Ressource.

Rückgabewert

Typ
AsyncDisposableStack
Beschreibung
Eine neue AsyncDisposableStack-Instanz mit einem leeren Stapel.

Beispiele

Grundlegende Verwendung mit await using

// Simulierter Datenbankverbindungs-Typ mit asyncDispose
const createDbConnection = async () => {
  const conn = {
    query: async (sql) => ({ rows: [{ id: 1 }] }),
    [Symbol.asyncDispose]: async () => {
      console.log("Datenbankverbindung wird geschlossen...");
      // await conn.close();
    }
  };
  return conn;
};

async function main() {
  await using stack = new AsyncDisposableStack();

  // Datenbankverbindung registrieren
  const db = stack.use(await createDbConnection());

  const result = await db.query("SELECT * FROM users");
  console.log("Ergebnis:", result.rows);

  // Am Ende des Blocks / bei Verlassen des Scopes wird stack.disposeAsync() automatisch aufgerufen
}

await main();
// Ausgabe:
// Ergebnis: [{ id: 1 }]
// Datenbankverbindung wird geschlossen...
Ergebnis: [{ id: 1 }] Datenbankverbindung wird geschlossen...

adopt() und defer() für externe Ressourcen

// Simulierter File-Handle ohne Symbol.asyncDispose
const openFile = async (path) => {
  console.log(`Datei geöffnet: ${path}`);
  return { path, fd: 42 };
};

const closeFile = async (handle) => {
  console.log(`Datei geschlossen: ${handle.path} (fd=${handle.fd})`);
};

async function processFiles() {
  await using stack = new AsyncDisposableStack();

  // adopt(): Ressource + eigene Cleanup-Funktion
  const fileA = stack.adopt(await openFile("a.txt"), closeFile);
  const fileB = stack.adopt(await openFile("b.txt"), closeFile);

  // defer(): Beliebiger asynchroner Aufräum-Callback
  stack.defer(async () => {
    console.log("Temporäre Dateien werden gelöscht...");
  });

  console.log(`Verarbeite: ${fileA.path} und ${fileB.path}`);
  // Beim Verlassen: defer zuerst (LIFO), dann b.txt, dann a.txt
}

await processFiles();
Datei geöffnet: a.txt Datei geöffnet: b.txt Verarbeite: a.txt und b.txt Temporäre Dateien werden gelöscht... Datei geschlossen: b.txt (fd=42) Datei geschlossen: a.txt (fd=42)

Manuelles disposeAsync() und move()

async function example() {
  const stack = new AsyncDisposableStack();

  stack.defer(async () => console.log("Cleanup A"));
  stack.defer(async () => console.log("Cleanup B"));

  console.log("Disposed:", stack.disposed); // false

  // Eigentümerschaft an einen anderen Stack übertragen
  const outerStack = stack.move();
  console.log("Alter Stack disposed:", stack.disposed); // true (übertragen)

  // Manuell aufräumen
  await outerStack.disposeAsync();
  console.log("Outer Stack disposed:", outerStack.disposed); // true
}

await example();
Disposed: false Alter Stack disposed: true Cleanup B Cleanup A Outer Stack disposed: true

// Wichtig · Fallstricke

Browser-Kompatibilität: AsyncDisposableStack ist Teil von ECMAScript 2025 und zum Zeitpunkt dieser Dokumentation noch nicht in allen Browsern standardmäßig verfügbar. Node.js unterstützt es ab Version 22+ hinter einem Flag bzw. ab Node.js 23+ nativ. Prüfe die aktuelle Browser-Kompatibilität auf caniuse.com oder MDN.

Fehlerbehandlung: Wenn mehrere Disposer Fehler werfen, werden diese als SuppressedError verschachtelt – der neueste Fehler ist in error, der ältere in suppressed. Alle Disposer werden immer ausgeführt, auch wenn vorherige fehlschlagen.

Einmaliger Aufruf: Nach dem Aufruf von disposeAsync() ist der Stack als disposed markiert; weitere Aufrufe von disposeAsync() sind ein No-op. Versuche, nach dem Dispose neue Ressourcen zu registrieren, werfen einen ReferenceError.

Synchrone Alternative: Für rein synchrone Ressourcen steht DisposableStack mit using (ohne await) zur Verfügung.