Signatur
Beschreibung
defer() legt eine beliebige Rückruffunktion auf einen DisposableStack. Beim Entsorgen des Stacks wird sie ohne Argumente aufgerufen. Das geschieht entweder explizit über stack.dispose() oder automatisch, sobald ein mit using deklarierter Block verlassen wird. So verhält sich defer() ähnlich wie defer in Go oder ein finally-Block, nur dass sich die Aufräumlogik direkt neben der Stelle registrieren lässt, an der die Ressource angelegt wird.
Die Methode eignet sich besonders für Ressourcen, die kein [Symbol.dispose]() implementieren, zum Beispiel Event-Listener, Timer, temporäre DOM-Änderungen oder Handles aus älteren APIs. Für Objekte, die bereits entsorgbar sind, verwendest du use(). Für einen Wert mit einer eigenen Aufräumfunktion ist adopt() gedacht.
Alle Rückrufe auf dem Stack werden in umgekehrter Registrierungsreihenfolge (LIFO) ausgeführt. Was zuletzt angelegt wurde, wird also zuerst freigegeben. Auf diese Weise bleiben Abhängigkeiten zwischen Ressourcen beim Aufräumen korrekt erhalten.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $onDispose Pflicht | () => void | Funktion, die beim Entsorgen des Stacks ohne Argumente und mit this gleich undefined aufgerufen wird. Ihr Rückgabewert wird ignoriert. |
Rückgabewert
use() und adopt() liefert sie keinen Wert, den man weiterverwenden könnte.Beispiele
Aufräumen mit using und LIFO-Reihenfolge
function verarbeite() {
using stack = new DisposableStack();
console.log('Verbindung öffnen');
stack.defer(() => console.log('Verbindung schließen'));
console.log('Transaktion starten');
stack.defer(() => console.log('Transaktion beenden'));
console.log('Arbeite ...');
} // hier wird stack automatisch entsorgt
verarbeite();
Event-Listener und Timer zuverlässig entfernen
const beobachteFenster = () => {
const stack = new DisposableStack();
const onResize = () => console.log('Größe:', window.innerWidth);
window.addEventListener('resize', onResize);
stack.defer(() => window.removeEventListener('resize', onResize));
const id = setInterval(() => console.log('Tick'), 1000);
stack.defer(() => clearInterval(id));
// Aufrufer erhält die Verantwortung fürs Aufräumen
return stack;
};
const aufraeumen = beobachteFenster();
setTimeout(() => {
aufraeumen.dispose();
console.log('Alles entfernt:', aufraeumen.disposed);
}, 3500);
Aufräumen nur im Fehlerfall mit move()
class Dienst {
#stack;
constructor() {
using stack = new DisposableStack();
const cache = new Map();
stack.defer(() => { cache.clear(); console.log('Cache geleert'); });
if (Math.random() < 0) throw new Error('Init fehlgeschlagen');
// Erfolg: Ressourcen an die Instanz übergeben, lokal nicht entsorgen
this.#stack = stack.move();
}
[Symbol.dispose]() {
this.#stack.dispose();
}
}
{
using dienst = new Dienst();
console.log('Dienst läuft');
}
// Wichtig · Fallstricke
- Bereits entsorgter Stack: Ein Aufruf von
defer()nachdispose()odermove()wirft einenReferenceError. - Kein Callable: Ist
onDisposekeine Funktion, wird sofort einTypeErrorgeworfen, also schon beim Registrieren und nicht erst beim Entsorgen. - Fehler in Rückrufen: Wirft ein Rückruf, laufen die übrigen trotzdem weiter. Treten mehrere Fehler auf, werden sie in einem
SuppressedErrorverkettet. - Asynchrones Aufräumen: Der Rückgabewert wird ignoriert, ein zurückgegebenes Promise wird also nicht abgewartet. Für asynchrones Aufräumen verwendest du
AsyncDisposableStack.prototype.defer()zusammen mitawait using. - Keine Argumente: Anders als bei
adopt()erhält der Rückruf keinen Wert. Benötigte Daten musst du per Closure einschließen. - Kompatibilität: Die Methode gehört zu den noch recht neuen Funktionen für explizites Ressourcenmanagement (Chrome/Edge 134+, Firefox 134+, Node.js 24+). Safari unterstützt sie noch nicht vollständig. Bei Bedarf helfen Polyfills wie
core-jsoder Transpiler wie TypeScript 5.2+ bzw. Babel.