Signatur
Beschreibung
disposeAsync() räumt einen AsyncDisposableStack auf. Alle Ressourcen und Aufräum-Callbacks, die du vorher mit use(), adopt() oder defer() registriert hast, werden in umgekehrter Reihenfolge (LIFO) aufgerufen. Die Methode wartet jeden Disposer mit await ab, bevor sie den nächsten startet. Das Aufräumen läuft also streng nacheinander und nicht parallel.
Du brauchst die Methode, wenn du mehrere asynchrone Ressourcen wie Datenbankverbindungen, Dateihandles oder Streams gemeinsam verwaltest und sie gezielt zu einem bestimmten Zeitpunkt freigeben willst. Meist rufst du sie aber gar nicht direkt auf: Mit await using stack = new AsyncDisposableStack() übernimmt die Laufzeit den Aufruf automatisch am Ende des Blocks. disposeAsync() ist dieselbe Funktion wie AsyncDisposableStack.prototype[Symbol.asyncDispose].
Nach dem Aufruf gilt der Stack als entsorgt (stack.disposed === true). Ein zweiter Aufruf von disposeAsync() tut nichts und erfüllt sich sofort mit undefined. Andere Methoden wie use(), defer() oder move() werfen danach dagegen einen ReferenceError.
Wirft ein Disposer einen Fehler, werden die übrigen Disposer trotzdem noch aufgerufen. Am Ende wird das Promise mit dem Fehler zurückgewiesen. Treten mehrere Fehler auf, werden sie in SuppressedError-Objekte verschachtelt: error enthält den zuletzt aufgetretenen Fehler, suppressed den vorherigen.
Rückgabewert
undefined erfüllt, sobald alle Disposer fertig sind. Hat mindestens ein Disposer einen Fehler geworfen, wird das Promise zurückgewiesen, bei mehreren Fehlern mit einem SuppressedError.Beispiele
Reihenfolge des Aufräumens (LIFO)
const stack = new AsyncDisposableStack();
const warte = (ms) => new Promise((r) => setTimeout(r, ms));
stack.defer(async () => { await warte(50); console.log('1. registriert: Logger schließen'); });
stack.defer(async () => { await warte(10); console.log('2. registriert: DB trennen'); });
stack.defer(() => console.log('3. registriert: Cache leeren'));
await stack.disposeAsync();
console.log('disposed:', stack.disposed);
// Zweiter Aufruf tut nichts
await stack.disposeAsync();
Mehrere Ressourcen verwalten und gezielt freigeben
const oeffneVerbindung = async (name) => {
console.log(`${name} geöffnet`);
return {
name,
query: async (sql) => `${name}: ${sql} -> ok`,
close: async () => console.log(`${name} geschlossen`),
};
};
const stack = new AsyncDisposableStack();
try {
// adopt(): Wert + eigene Aufräumfunktion registrieren
const primaer = stack.adopt(await oeffneVerbindung('primary'), (c) => c.close());
const replika = stack.adopt(await oeffneVerbindung('replica'), (c) => c.close());
console.log(await primaer.query('SELECT 1'));
console.log(await replika.query('SELECT 2'));
} finally {
await stack.disposeAsync();
}
Fehlerbehandlung mit SuppressedError
const stack = new AsyncDisposableStack();
stack.defer(async () => { throw new Error('Datei konnte nicht geschlossen werden'); });
stack.defer(async () => { throw new Error('Socket-Timeout'); });
stack.defer(() => console.log('Läuft trotzdem'));
try {
await stack.disposeAsync();
} catch (e) {
console.log(e instanceof SuppressedError);
console.log('error:', e.error.message);
console.log('suppressed:', e.suppressed.message);
}
Automatisch mit await using
async function verarbeite() {
await using stack = new AsyncDisposableStack();
stack.defer(async () => console.log('Aufgeräumt'));
console.log('Arbeite …');
// Am Blockende ruft die Laufzeit stack[Symbol.asyncDispose]() auf,
// das ist identisch mit disposeAsync()
}
await verarbeite();
// Wichtig · Fallstricke
- Kompatibilität: Die Methode gehört zum Vorschlag „Explicit Resource Management“ und ist noch neu. Unterstützt wird sie ab Chrome/Edge 134, Firefox 141, Node.js 24 und Deno. Safari unterstützt sie noch nicht. Für ältere Umgebungen gibt es Polyfills, z. B.
core-js. Die Syntaxawait usingbraucht allerdings nativen Support oder einen Transpiler wie TypeScript 5.2+ oder Babel. - Immer mit
awaitaufrufen. Ohneawaitläuft das Aufräumen im Hintergrund weiter. Fehler landen dann als unbehandelte Promise-Zurückweisung (unhandled rejection) in der Konsole. - Nacheinander, nicht parallel: Die Disposer laufen streng hintereinander. Viele langsame Disposer summieren ihre Laufzeiten. Wenn du parallel aufräumen willst, registriere einen einzelnen
defer()-Callback mitPromise.all(). - Nach dem Entsorgen werfen
use(),adopt(),defer()undmove()einenReferenceError. NurdisposeAsync()selbst darf mehrfach aufgerufen werden. - Hast du die Ressourcen vorher mit
move()auf einen neuen Stack übertragen, entsorgtdisposeAsync()auf dem alten Stack nichts mehr. - Auch synchrone Disposables (
Symbol.dispose) und synchronedefer()-Callbacks sind erlaubt. Ihr Ergebnis wird trotzdem mitawaitabgewartet.