Signatur
Beschreibung
Mit new AsyncDisposableStack() erzeugst du einen zunächst leeren Container für Aufräumarbeiten. Über use(), adopt() und defer() registrierst du Ressourcen oder Aufräum-Callbacks. Rufst du disposeAsync() auf, werden alle Einträge in umgekehrter Reihenfolge (LIFO) abgearbeitet. Jeder Aufruf wird mit await abgewartet, bevor der nächste startet.
Am häufigsten kombinierst du den Stapel mit der Deklaration await using: await using stack = new AsyncDisposableStack();. Verlässt das Programm den Block, auch durch eine Ausnahme, räumt der Stapel automatisch auf, weil er selbst [Symbol.asyncDispose]() implementiert.
Das lohnt sich, wenn du mehrere Ressourcen nacheinander öffnest, zum Beispiel Datenbankverbindung, Dateihandle und Lock, oder wenn Objekte kein Symbol.asyncDispose haben. Mit move() kannst du die Verantwortung für die gesammelten Ressourcen an einen neuen Stapel übergeben. So baust du etwa einen Konstruktor, der nur bei Erfolg die Ressourcen behält und sie bei einem Fehler sofort freigibt.
Rückgabewert
AsyncDisposableStack-Objekt. Seine Eigenschaft disposed steht zu Beginn auf false.Beispiele
Stapel erzeugen und Aufräumarbeiten registrieren
const stack = new AsyncDisposableStack();
stack.defer(async () => {
console.log('Verbindung geschlossen');
});
stack.defer(async () => {
console.log('Cache geleert');
});
console.log(stack.disposed);
await stack.disposeAsync();
console.log(stack.disposed);
Automatisches Aufräumen mit await using
const openConnection = async (name) => ({
name,
async [Symbol.asyncDispose]() {
console.log(`${name} geschlossen`);
}
});
const run = async () => {
await using stack = new AsyncDisposableStack();
const db = stack.use(await openConnection('DB'));
const cache = stack.use(await openConnection('Cache'));
console.log(`Arbeite mit ${db.name} und ${cache.name}`);
}; // Hier wird automatisch aufgeräumt
await run();
Ressourcen ohne Symbol.asyncDispose per adopt() einbinden
import { open } from 'node:fs/promises';
const readFirstLine = async (path) => {
await using stack = new AsyncDisposableStack();
const handle = stack.adopt(await open(path), (h) => h.close());
const { buffer, bytesRead } = await handle.read(Buffer.alloc(256), 0, 256, 0);
return buffer.toString('utf8', 0, bytesRead).split('\n')[0];
};
console.log(await readFirstLine('./package.json'));
Verantwortung mit move() übertragen
const createService = async () => {
await using stack = new AsyncDisposableStack();
stack.defer(async () => console.log('Ressource A freigegeben'));
stack.defer(async () => console.log('Ressource B freigegeben'));
// Hätte es bis hier einen Fehler gegeben, wäre sofort aufgeräumt worden.
const owned = stack.move();
return {
async [Symbol.asyncDispose]() { await owned.disposeAsync(); }
};
};
const service = await createService();
console.log('Service läuft');
await service[Symbol.asyncDispose]();
// Wichtig · Fallstricke
- Nur mit
newaufrufbar:AsyncDisposableStack()ohnenewwirft einenTypeError. Der Konstruktor nimmt keine Argumente entgegen. - Kompatibilität: Die Methode gehört zum Vorschlag „Explicit Resource Management“. Unterstützt wird sie ab Chrome/Edge 134, Firefox 141 und Node.js 24, Safari fehlt noch. Für ältere Umgebungen gibt es Polyfills (z. B.
core-js), undawait usinglässt sich mit TypeScript 5.2+ oder Babel transpilieren. - Nach dem Aufräumen gesperrt: Ist der Stapel einmal aufgeräumt (
disposed === true), werfenuse(),adopt(),defer()undmove()einenReferenceError. Ein erneuter Aufruf vondisposeAsync()bewirkt dagegen nichts. - Fehler beim Aufräumen: Werfen mehrere Callbacks, laufen trotzdem alle restlichen weiter. Am Ende werden die Fehler zu einem
SuppressedErrorverschachtelt. - Nacheinander, nicht parallel: Die Callbacks werden der Reihe nach abgewartet. Sehr viele langsame Freigaben summieren sich daher zeitlich.
- Für rein synchrone Ressourcen nimmst du
DisposableStackmitusing. Das ist schlanker, weil keine Promises nötig sind.