Signatur
Beschreibung
move() erzeugt eine neue DisposableStack-Instanz, die alle bisher mit use(), adopt() oder defer() registrierten Aufräum-Callbacks in derselben Reihenfolge übernimmt. Danach gilt der ursprüngliche Stack als entsorgt (disposed === true). Seine Disposer werden dabei aber nicht aufgerufen, die Verantwortung dafür geht vollständig an den neuen Stack über.
Gebraucht wird das vor allem zusammen mit using: Eine Funktion oder ein Konstruktor legt mehrere Ressourcen nacheinander an und bindet einen Stack mit using. Wirft ein späterer Schritt einen Fehler, räumt using automatisch alles auf, was bis dahin angelegt wurde. Geht alles gut, ruft man am Ende stack.move() auf. Der mit using gebundene Stack ist dann leer bzw. schon entsorgt, und der neue Stack wird zurückgegeben oder in einem Objekt gespeichert, damit die Ressourcen später freigegeben werden können.
So lässt sich das Muster „alles oder nichts“ beim Anlegen von Ressourcen umsetzen, ohne eigene try/catch-Blöcke zu schreiben. Ein typischer Einsatz ist eine Klasse, die [Symbol.dispose]() implementiert und intern einen per move() übernommenen Stack hält.
Rückgabewert
DisposableStack, der alle Disposer des ursprünglichen Stacks in unveränderter Reihenfolge enthält. Der ursprüngliche Stack ist danach entsorgt.Beispiele
Grundprinzip: Disposer übertragen
const stack = new DisposableStack();
stack.defer(() => console.log('Ressource A freigegeben'));
stack.defer(() => console.log('Ressource B freigegeben'));
const moved = stack.move();
console.log(stack.disposed); // alter Stack ist entsorgt
console.log(moved.disposed); // neuer Stack ist aktiv
moved.dispose(); // LIFO-Reihenfolge
Alles-oder-nichts-Initialisierung in einer Klasse
const openResource = (name) => {
console.log(`${name} geöffnet`);
return { name, [Symbol.dispose]() { console.log(`${name} geschlossen`); } };
};
class Connection {
#stack;
constructor(failAtCache = false) {
using stack = new DisposableStack();
this.socket = stack.use(openResource('Socket'));
if (failAtCache) throw new Error('Cache nicht verfügbar');
this.cache = stack.use(openResource('Cache'));
// Erfolg: Besitz an die Instanz übergeben
this.#stack = stack.move();
}
[Symbol.dispose]() {
this.#stack.dispose();
}
}
try {
new Connection(true);
} catch (e) {
console.log('Fehler:', e.message);
}
{
using conn = new Connection();
console.log('Arbeite mit Verbindung');
}
Ressourcen aus einer Fabrikfunktion zurückgeben
const createTempFiles = (count) => {
using stack = new DisposableStack();
const files = [];
for (let i = 0; i < count; i++) {
const file = `tmp-${i}.txt`;
files.push(file);
stack.defer(() => console.log(`${file} gelöscht`));
}
return { files, cleanup: stack.move() };
};
const { files, cleanup } = createTempFiles(2);
console.log(files);
cleanup.dispose();
// Wichtig · Fallstricke
- Wurde der Stack bereits entsorgt (durch
dispose()oder ein vorherigesmove()), wirftmove()einenReferenceError. - Nach
move()werfen alle weiteren Aufrufe vonuse(),adopt(),defer()undmove()auf dem alten Stack.dispose()dagegen ist auf einem entsorgten Stack erlaubt und tut einfach nichts. Deshalb kannusingden alten Stack am Blockende problemlos „entsorgen“. - Wer den Rückgabewert von
move()weder speichert noch zurückgibt, verliert die Disposer. Die Ressourcen werden dann nie freigegeben (Ressourcenleck). - Für asynchrone Aufräumarbeiten gibt es
AsyncDisposableStack.prototype.move(), das genauso funktioniert. - Kompatibilität: Die Methode gehört zum Vorschlag „Explicit Resource Management“ und ist in Chromium 134+, Firefox 134+ und Node.js 24+ verfügbar, in Safari aber noch nicht. Für ältere Umgebungen gibt es Polyfills, z. B.
core-js. Dieusing-Syntax braucht außerdem Unterstützung in der Engine oder einen Transpiler (TypeScript 5.2+, Babel).