Start · Sprachen · JavaScript · Referenz · DisposableStack.prototype.move()

DisposableStack.prototype.move()

Methode

Überträgt alle registrierten Disposer in einen neuen <code>DisposableStack</code> und markiert den ursprünglichen Stack als entsorgt, ohne die Disposer auszuführen.

seit JavaScript ES2026 (Explicit Resource Management); Chrome 134+ Kategorie: method-property

Signatur

DisposableStack.prototype.move()

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

Typ
DisposableStack
Beschreibung
Ein neuer 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
true false Ressource B freigegeben Ressource A freigegeben

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');
}
Socket geöffnet Socket geschlossen Fehler: Cache nicht verfügbar Socket geöffnet Cache geöffnet Arbeite mit Verbindung Cache geschlossen Socket geschlossen

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();
[ 'tmp-0.txt', 'tmp-1.txt' ] tmp-1.txt gelöscht tmp-0.txt gelöscht

// Wichtig · Fallstricke

  • Wurde der Stack bereits entsorgt (durch dispose() oder ein vorheriges move()), wirft move() einen ReferenceError.
  • Nach move() werfen alle weiteren Aufrufe von use(), adopt(), defer() und move() auf dem alten Stack. dispose() dagegen ist auf einem entsorgten Stack erlaubt und tut einfach nichts. Deshalb kann using den 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. Die using-Syntax braucht außerdem Unterstützung in der Engine oder einen Transpiler (TypeScript 5.2+, Babel).