Start · Sprachen · JavaScript · Referenz · DisposableStack

DisposableStack

Klasse

Verwaltet einen Stapel von Ressourcen oder Cleanup-Callbacks, die beim Entsorgen des Stapels automatisch in umgekehrter Reihenfolge ausgeführt werden.

seit JavaScript ES2026 (Stage 3 Proposal; experimentell in V8/Node Kategorie: core

Signatur

class DisposableStack

Beschreibung

DisposableStack ist Teil des TC39-Proposals Explicit Resource Management und ermöglicht strukturiertes, vorhersehbares Freigeben von Ressourcen nach dem LIFO-Prinzip (Last In, First Out). Ressourcen werden dem Stapel hinzugefügt und beim Aufruf von dispose() – oder automatisch beim Verlassen eines using-Blocks – in umgekehrter Reihenfolge bereinigt.

Typische Anwendungsfälle sind das Schließen von Datenbankverbindungen, Entfernen von Event-Listenern, Freigeben nativer Handles oder das Zusammenfassen mehrerer Cleanup-Schritte in einem einzigen verwaltbaren Objekt. Durch die Kombination mit der using-Deklaration (ebenfalls Teil desselben Proposals) wird die Ressourcenverwaltung erheblich vereinfacht und Fehler durch vergessene Cleanups werden vermieden.

DisposableStack implementiert das Symbol.dispose-Protokoll und kann daher selbst als Ressource in einem using-Block verwendet werden. Intern werden alle registrierten Einträge in einem Stapel gehalten; beim Entsorgen werden sie der Reihe nach von oben nach unten (also in umgekehrter Registrierungsreihenfolge) aufgerufen.

Für asynchrone Cleanup-Operationen steht das Gegenstück AsyncDisposableStack zur Verfügung, das Symbol.asyncDispose implementiert und mit await using verwendet werden kann.

Rückgabewert

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

Beispiele

Ressourcen mit use() registrieren und automatisch bereinigen

// Simulierte Ressource mit [Symbol.dispose]
class DatabaseConnection {
  constructor(name) {
    this.name = name;
    console.log(`Verbindung geöffnet: ${this.name}`);
  }
  [Symbol.dispose]() {
    console.log(`Verbindung geschlossen: ${this.name}`);
  }
}

// using-Block: DisposableStack wird am Ende automatisch disposed
{
  using stack = new DisposableStack();

  const conn1 = stack.use(new DatabaseConnection('primary'));
  const conn2 = stack.use(new DatabaseConnection('replica'));

  console.log('Arbeite mit Verbindungen...');
  // Am Ende des Blocks: conn2 wird zuerst geschlossen, dann conn1
}
Verbindung geöffnet: primary Verbindung geöffnet: replica Arbeite mit Verbindungen... Verbindung geschlossen: replica Verbindung geschlossen: primary

Kombination von use(), adopt() und defer()

const cleanupLog = [];

// Ressource mit eigenem [Symbol.dispose]
const managedRes = {
  [Symbol.dispose]() { cleanupLog.push('managedRes disposed'); }
};

// Rohe Ressource ohne Dispose-Protokoll
const rawHandle = { id: 42 };

const stack = new DisposableStack();

// use(): Ressource mit [Symbol.dispose] registrieren
stack.use(managedRes);

// adopt(): Rohe Ressource + separater Cleanup-Callback
stack.adopt(rawHandle, (handle) => {
  cleanupLog.push(`rawHandle ${handle.id} freigegeben`);
});

// defer(): Reiner Callback ohne Ressource
stack.defer(() => {
  cleanupLog.push('defer-Callback ausgeführt');
});

console.log('Vor dispose:', stack.disposed); // false
stack.dispose();
console.log('Nach dispose:', stack.disposed); // true
console.log(cleanupLog);

// Zweiter Aufruf ist eine No-Op
stack.dispose();
console.log('Kein Fehler beim zweiten dispose-Aufruf');
Vor dispose: false Nach dispose: true ['defer-Callback ausgeführt', 'rawHandle 42 freigegeben', 'managedRes disposed'] Kein Fehler beim zweiten dispose-Aufruf

Stapel verschieben mit move()

function createResources() {
  // Erstellt einen Stack und überträgt Verantwortung an den Aufrufer
  const stack = new DisposableStack();
  stack.defer(() => console.log('Ressource A bereinigt'));
  stack.defer(() => console.log('Ressource B bereinigt'));

  // move() überträgt alle Einträge in einen neuen Stack
  // und markiert den ursprünglichen als disposed
  return stack.move();
}

{
  using resources = createResources();
  console.log('Ressourcen aktiv...');
  // Beim Verlassen des Blocks: automatisches dispose()
}
Ressourcen aktiv... Ressource B bereinigt Ressource A bereinigt

// Wichtig · Fallstricke

Experimenteller Status: DisposableStack befindet sich im TC39-Prozess auf Stage 3 (Stand 2024) und ist in V8 ab Version 12.x sowie Node.js 22+ hinter dem Flag --harmony-explicit-resource-management oder nativ ab Node.js 22.4+ verfügbar. TypeScript unterstützt das Feature ab Version 5.2. Produktionseinsatz sollte die jeweiligen Zielumgebungen prüfen.

Fehlerverhalten: Wenn ein Cleanup-Callback eine Exception wirft, werden die restlichen Callbacks trotzdem ausgeführt. Mehrere Exceptions werden zu einem SuppressedError zusammengefasst, das sowohl den ursprünglichen Fehler als auch den unterdrückten Fehler enthält.

Idempotenz: Ein bereits entsorgter Stack ignoriert weitere dispose()-Aufrufe stillschweigend. Der Zustand lässt sich über die disposed-Eigenschaft abfragen.

Asynchrone Operationen: Für Cleanups, die await benötigen, muss AsyncDisposableStack mit await using verwendet werden. Das synchrone DisposableStack unterstützt keine asynchronen Callbacks.