Start · Sprachen · JavaScript · Referenz · Promise.withResolvers()

Promise.withResolvers()

Methode

Erzeugt ein neues Promise und gibt es zusammen mit seinen Funktionen <code>resolve</code> und <code>reject</code> als Objekt <code>{ promise, resolve, reject }</code> zurück.

seit JavaScript ES2024 (Baseline 2024: Chrome 119, Firefox 121, Sa Kategorie: method-property

Signatur

Promise.withResolvers()

Beschreibung

Promise.withResolvers() ist eine statische Methode, die ein neues Promise erzeugt und die beiden Steuerfunktionen resolve und reject außerhalb der Executor-Funktion verfügbar macht. Das Ergebnis ist ein einfaches Objekt der Form { promise, resolve, reject }.

Die Methode ersetzt das bekannte „Deferred“-Muster, bei dem man die Resolver-Funktionen im Konstruktor in äußere Variablen kopiert hat:

  • let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; });

Das ist praktisch, wenn das Erfüllen oder Ablehnen eines Promise von einem Ereignis abhängt, das an anderer Stelle im Code eintritt – etwa bei Event-Listenern, Stream-Callbacks, Warteschlangen, Nachrichten-Protokollen (WebSocket, postMessage) oder beim Umwandeln von Callback-APIs, deren Callback mehrfach bzw. verzögert aufgerufen wird.

Wie beim Konstruktor gilt: Ein Promise wird nur einmal abgeschlossen. Weitere Aufrufe von resolve oder reject werden stillschweigend ignoriert. Wird resolve mit einem anderen Promise oder Thenable aufgerufen, übernimmt das Promise dessen Zustand.

Rückgabewert

Typ
{ promise: Promise<T>, resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void }
Beschreibung

Ein einfaches Objekt mit den Eigenschaften promise (das neue, noch ausstehende Promise), resolve (erfüllt das Promise bzw. übernimmt den Zustand eines übergebenen Thenables) und reject (lehnt das Promise mit dem übergebenen Grund ab).

Beispiele

Grundprinzip: Promise von außen erfüllen

const { promise, resolve, reject } = Promise.withResolvers();

promise.then((wert) => console.log('Erfüllt mit:', wert));

// Irgendwo später im Code
setTimeout(() => resolve(42), 500);
Erfüllt mit: 42

Auf ein einmaliges DOM-Ereignis warten

const warteAufKlick = (element, timeoutMs = 5000) => {
  const { promise, resolve, reject } = Promise.withResolvers();

  const onClick = (event) => {
    clearTimeout(timer);
    resolve(event);
  };
  const timer = setTimeout(() => {
    element.removeEventListener('click', onClick);
    reject(new Error('Kein Klick innerhalb des Zeitlimits'));
  }, timeoutMs);

  element.addEventListener('click', onClick, { once: true });
  return promise;
};

const button = document.querySelector('#bestaetigen');
try {
  const event = await warteAufKlick(button);
  console.log('Geklickt bei x =', event.clientX);
} catch (err) {
  console.warn(err.message);
}
Geklickt bei x = 128 (oder: Kein Klick innerhalb des Zeitlimits)

Node.js-Stream in ein Promise umwandeln

import { createReadStream } from 'node:fs';

const leseDatei = (pfad) => {
  const { promise, resolve, reject } = Promise.withResolvers();
  const teile = [];

  createReadStream(pfad, { encoding: 'utf8' })
    .on('data', (chunk) => teile.push(chunk))
    .on('end', () => resolve(teile.join('')))
    .on('error', reject);

  return promise;
};

const inhalt = await leseDatei('./package.json');
console.log(JSON.parse(inhalt).name);
mein-projekt

Anfrage-Antwort über WebSocket zuordnen

const offen = new Map();
let naechsteId = 1;
const socket = new WebSocket('wss://example.com/rpc');

socket.addEventListener('message', ({ data }) => {
  const { id, result, error } = JSON.parse(data);
  const eintrag = offen.get(id);
  if (!eintrag) return;
  offen.delete(id);
  error ? eintrag.reject(new Error(error)) : eintrag.resolve(result);
});

const rufeAuf = (methode, params) => {
  const id = naechsteId++;
  const { promise, resolve, reject } = Promise.withResolvers();
  offen.set(id, { resolve, reject });
  socket.send(JSON.stringify({ id, methode, params }));
  return promise;
};

// Nutzung (nach dem open-Event):
// const summe = await rufeAuf('addiere', [2, 3]);

// Wichtig · Fallstricke

  • Kompatibilität: Neu in ES2024. Verfügbar ab Chrome/Edge 119, Firefox 121, Safari 17.4 und Node.js 22. Für ältere Umgebungen gibt es Polyfills (z. B. core-js), oder man nutzt das klassische Muster mit new Promise().
  • Kein automatisches Abfangen von Fehlern: Beim Konstruktor wird eine im Executor geworfene Ausnahme automatisch zu einer Ablehnung. Bei withResolvers() gibt es keinen Executor – Fehler in deinem Code musst du selbst abfangen und an reject weiterreichen, sonst bleibt das Promise für immer ausstehend.
  • Hängende Promises: Wird weder resolve noch reject aufgerufen, wartet jedes await darauf endlos. Für externe Ereignisse daher Zeitlimits oder ein AbortSignal einplanen und Listener aufräumen.
  • Nur einmal abschließbar: Nach dem ersten resolve/reject sind weitere Aufrufe wirkungslos und werfen keinen Fehler.
  • Kapselung: Gib nach außen möglichst nur promise weiter, nicht resolve/reject, damit fremder Code das Ergebnis nicht verändern kann.
  • Die Methode ist generisch: Mit Promise.withResolvers.call(MeinePromiseUnterklasse) wird eine Instanz der Unterklasse erzeugt. Ein Aufruf ohne gültigen Konstruktor als this wirft einen TypeError.