Promise()-Konstruktor
Baseline
Weitgehend verfügbar
Diese Funktion ist gut etabliert und funktioniert auf vielen Geräten und in vielen Browserversionen. Sie ist seit Juli 2015 browserübergreifend verfügbar.
Der Promise()-Konstruktor erstellt Promise-Objekte. Er wird hauptsächlich verwendet, um callback-basierte APIs zu umhüllen, die noch keine Unterstützung für Promises bieten.
Probieren Sie es aus
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 300);
});
promise1.then((value) => {
console.log(value);
// Expected output: "foo"
});
console.log(promise1);
// Expected output: [object Promise]
Syntax
new Promise(executor)
Parameter
executor-
Eine
Function, die vom Konstruktor ausgeführt wird. Sie erhält zwei Funktionen als Parameter:resolveFuncundrejectFunc. Alle Fehler, die imexecutorgeworfen werden, führen dazu, dass das Promise abgelehnt wird, und der Rückgabewert wird ignoriert. Die Semantik vonexecutorwird unten ausführlich beschrieben.
Rückgabewert
Beim Aufruf über new gibt der Promise-Konstruktor ein Promise-Objekt zurück. Das Promise-Objekt wird aufgelöst, wenn eine der Funktionen resolveFunc oder rejectFunc aufgerufen wird. Beachten Sie, dass wenn Sie resolveFunc aufrufen und ein anderes Promise-Objekt als Argument übergeben, das ursprüngliche Promise als "aufgelöst" bezeichnet werden kann, aber noch nicht "erledigt" ist. Siehe die Promise-Beschreibung für weitere Erklärungen.
Beschreibung
Traditionell (vor Promises) wurden asynchrone Aufgaben als Callbacks entworfen.
readFile("./data.txt", (error, result) => {
// This callback will be called when the task is done, with the
// final `error` or `result`. Any operation dependent on the
// result must be defined within this callback.
});
// Code here is immediately executed after the `readFile` request
// is fired. It does not wait for the callback to be called, hence
// making `readFile` "asynchronous".
Um die Lesbarkeit zu verbessern und die Sprachfunktionen von Promises zu nutzen, ermöglicht der Promise()-Konstruktor, eine callback-basierte API in eine promise-basierte zu transformieren.
Hinweis:
Wenn Ihre Aufgabe bereits auf Promises basiert, benötigen Sie wahrscheinlich den Promise()-Konstruktor nicht.
Der executor ist benutzerdefinierter Code, der ein Ergebnis in einem Callback mit einem Promise verknüpft. Sie als Programmierer schreiben den executor. Erwartet wird folgende Signatur:
function executor(resolveFunc, rejectFunc) {
// Typically, some asynchronous operation that accepts a callback,
// like the `readFile` function above
}
resolveFunc und rejectFunc sind ebenfalls Funktionen, und Sie können ihnen beliebige tatsächliche Namen geben. Ihre Signaturen sind einfach: Sie akzeptieren einen einzigen Parameter beliebigen Typs.
resolveFunc(value); // call on resolved
rejectFunc(reason); // call on rejected
Der value-Parameter, der an resolveFunc übergeben wird, kann ein weiteres Promise-Objekt sein, in welchem Fall der Zustand des neu erstellten Promises an das übergebene Promise "gebunden" wird (im Rahmen des Resolution Promises). Der rejectFunc hat eine Semantik, die der throw-Anweisung nahe kommt, daher ist reason typischerweise eine Error-Instanz. Wenn entweder value oder reason weggelassen wird, wird das Promise mit undefined erfüllt/abgelehnt.
Der Abschlusszustand des executor hat begrenzte Auswirkungen auf den Zustand des Promises:
- Der Rückgabewert des
executorwird ignoriert.return-Anweisungen innerhalb desexecutorhaben lediglich Auswirkungen auf den Kontrollfluss und ändern, ob ein Teil der Funktion ausgeführt wird, aber keinen Einfluss auf den Erfüllungswert des Promises. Wennexecutorbeendet wird und es unmöglich ist, dassresolveFuncoderrejectFuncin Zukunft aufgerufen werden können (zum Beispiel, wenn keine asynchronen Aufgaben geplant sind), bleibt das Promise für immer ausstehend. - Wenn im
executorein Fehler geworfen wird, wird das Promise abgelehnt, es sei denn,resolveFuncoderrejectFuncwurde bereits aufgerufen.
Hinweis: Die Existenz ausstehender Promises verhindert nicht, dass das Programm beendet wird. Wenn die Ereignisschleife leer ist, endet das Programm trotz ausstehender Promises (weil diese notwendigerweise für immer ausstehend sind).
Hier ist eine Zusammenfassung des typischen Ablaufs:
- Zu dem Zeitpunkt, an dem der Konstruktor das neue
Promise-Objekt generiert, erzeugt er auch ein entsprechendes Paar von Funktionen fürresolveFuncundrejectFunc; diese sind mit demPromise-Objekt "verbunden". executorumschließt in der Regel eine asynchrone Operation, die eine callback-basierte API bietet. Der Callback (der an die ursprüngliche callback-basierte API übergeben wird) ist imexecutor-Code definiert, sodass er Zugriff aufresolveFuncundrejectFunchat.- Der
executorwird synchron (sobald dasPromisekonstruiert ist) mit den FunktionenresolveFuncundrejectFuncals Argumente aufgerufen. - Der Code innerhalb des
executorhat die Möglichkeit, eine Operation auszuführen. Der abschließende Abschluss der asynchronen Aufgabe wird durch die Nebenwirkung vonresolveFuncoderrejectFuncan die Promise-Instanz kommuniziert. Die Nebenwirkung ist, dass dasPromise-Objekt "aufgelöst" wird.- Wenn
resolveFunczuerst aufgerufen wird, wird der übergebene Wert aufgelöst. Das Promise kann ausstehend bleiben (falls ein weiterer thenable übergeben wird), erfüllt werden (in den meisten Fällen, in denen ein nicht-thenables Wert übergeben wird) oder abgelehnt werden (bei einem ungültigen Auflösungswert). - Wenn
rejectFunczuerst aufgerufen wird, wird das Promise sofort abgelehnt. - Sobald eine der auflösenden Funktionen (
resolveFuncoderrejectFunc) aufgerufen wird, bleibt das Promise "aufgelöst". Nur der erste Aufruf vonresolveFuncoderrejectFuncbeeinflusst den endgültigen Zustand des Promises, und nachfolgende Aufrufe dieser Funktionen können weder den Erfüllungswert/Ablehnungsgrund ändern noch den endgültigen Zustand von "erfüllt" zu "abgelehnt" oder umgekehrt ändern. - Wenn der
executormit einem Fehler endet, wird das Promise abgelehnt. Der Fehler wird jedoch ignoriert, wenn eine der auflösenden Funktionen bereits aufgerufen wurde (sodass das Promise bereits aufgelöst ist). - Das Auflösen des Promises führt nicht notwendigerweise dazu, dass das Promise erfüllt oder abgelehnt wird (d.h. sich nicht setzt). Das Promise kann immer noch ausstehend sein, weil es mit einem anderen thenable aufgelöst wird, aber sein endgültiger Zustand wird dem des aufgelösten thenable entsprechen.
- Wenn
- Sobald das Promise sich gesetzt hat, ruft es (asynchron) die weiteren Handler auf, die über
then(),catch(), oderfinally()assoziiert sind. Der endgültige Erfüllungswert oder Ablehnungsgrund wird an die Aufrufe der Erfüllungs- und Ablehnungshandler als Eingabeparameter übergeben (siehe Verkettete Promises).
Beispielsweise kann die callback-basierte readFile-API oben in eine promise-basierte umgewandelt werden.
const readFilePromise = (path) =>
new Promise((resolve, reject) => {
readFile(path, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
readFilePromise("./data.txt")
.then((result) => console.log(result))
.catch((error) => console.error("Failed to read data"));
Die resolve- und reject-Callbacks sind nur im Rahmen der Executor-Funktion verfügbar, was bedeutet, dass Sie nicht mehr auf sie zugreifen können, nachdem das Promise konstruiert ist. Wenn Sie das Promise konstruieren möchten, bevor Sie entscheiden, wie es aufgelöst werden soll, können Sie stattdessen die Methode Promise.withResolvers() verwenden, die die resolve- und reject-Funktionen freigibt.
Die resolve-Funktion
Die resolve-Funktion hat folgende Eigenschaften:
- Wenn sie mit dem gleichen Wert wie das neu erstellte Promise aufgerufen wird (das Promise, mit dem es "verbunden" ist), wird das Promise mit einem
TypeErrorabgelehnt. - Wenn es mit einem nicht-thenable Wert aufgerufen wird (einem primitiven Wert oder einem Objekt, dessen
then-Eigenschaft nicht aufrufbar ist, einschließlich, wenn die Eigenschaft fehlt), wird das Promise sofort mit diesem Wert erfüllt. - Wenn es mit einem thenable-Wert aufgerufen wird (einschließlich einer anderen
Promise-Instanz), wird diethen-Methode des thenables gespeichert und in Zukunft aufgerufen (sie wird immer asynchron aufgerufen). Diethen-Methode wird mit zwei Callbacks aufgerufen, die zwei neue Funktionen mit den exakt gleichen Eigenschaften wie dieresolveFuncundrejectFuncsind, die an die Executor-Funktion übergeben werden. Wenn der Aufruf derthen-Methode einen Fehler wirft, wird das aktuelle Promise mit dem geworfenen Fehler abgelehnt.
Im letzten Fall bedeutet dies, dass Code wie:
new Promise((resolve, reject) => {
resolve(thenable);
});
In etwa gleichbedeutend ist mit:
new Promise((resolve, reject) => {
try {
thenable.then(
(value) => resolve(value),
(reason) => reject(reason),
);
} catch (e) {
reject(e);
}
});
Außer dass im Fall von resolve(thenable):
resolvewird synchron aufgerufen, sodass das erneute Aufrufen vonresolveoderrejectkeine Wirkung hat, selbst wenn die überanotherPromise.then()angehängten Handler noch nicht aufgerufen wurden.- Die
then-Methode wird asynchron aufgerufen, sodass das Promise nie sofort aufgelöst wird, wenn ein thenable übergeben wird.
Da resolve erneut mit dem, was thenable.then() als value übergibt, aufgerufen wird, kann die Resolve-Funktion verschachtelte thenables "abflachen", bei denen ein thenable seinen onFulfilled-Handler mit einem weiteren thenable aufruft. Der Effekt ist, dass der Erfüllungs-Handler eines echten Promises nie ein thenable als Erfüllungswert erhält.
Beispiele
>Eine callback-basierte API in eine promise-basierte umwandeln
Um einer Funktion Promise-Funktionalität bereitzustellen, lassen Sie sie ein Promise zurückgeben, indem Sie die resolve- und reject-Funktionen zu den richtigen Zeitpunkten aufrufen.
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
Effekt des Aufrufs von resolveFunc
Das Aufrufen von resolveFunc führt dazu, dass das Promise aufgelöst wird, sodass das erneute Aufrufen von resolveFunc oder rejectFunc keine Wirkung hat. Allerdings kann das Promise in einem der Zustände sein: ausstehend, erfüllt oder abgelehnt.
Dieses pendingResolved-Promise wird zu dem Zeitpunkt aufgelöst, an dem es erstellt wird, da es bereits "gebunden" ist, den endgültigen Zustand des inneren Promises zu übernehmen, und das spätere Aufrufen von resolveOuter oder rejectOuter oder das Werfen eines Fehlers im Executor hat keinen Einfluss auf seinen endgültigen Zustand. Allerdings ist das innere Promise 100ms später noch ausstehend, sodass das äußere Promise ebenfalls ausstehend ist:
const pendingResolved = new Promise((resolveOuter, rejectOuter) => {
resolveOuter(
new Promise((resolveInner) => {
setTimeout(() => {
resolveInner("inner");
}, 100);
}),
);
});
Dieses fulfilledResolved-Promise wird in dem Moment erfüllt, in dem es aufgelöst wird, da es mit einem nicht-thenable-Wert aufgelöst wird. Bei der Erstellung ist es jedoch ungelöst, da weder resolve noch reject aufgerufen wurden. Ein ungelöstes Promise ist notwendigerweise ausstehend:
const fulfilledResolved = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("outer");
}, 100);
});
Das Aufrufen von rejectFunc führt offensichtlich dazu, dass das Promise abgelehnt wird. Allerdings gibt es auch zwei Möglichkeiten, das Promise sofort abzulehnen, selbst wenn der resolveFunc-Callback aufgerufen wird.
// 1. Resolving with the promise itself
const rejectedResolved1 = new Promise((resolve) => {
// Note: resolve has to be called asynchronously,
// so that the rejectedResolved1 variable is initialized
setTimeout(() => resolve(rejectedResolved1)); // TypeError: Chaining cycle detected for promise #<Promise>
});
// 2. Resolving with an object which throws when accessing the `then` property
const rejectedResolved2 = new Promise((resolve) => {
resolve({
get then() {
throw new Error("Can't get then property");
},
});
});
Spezifikationen
| Spezifikation |
|---|
| ECMAScript® 2027 Language Specification> # sec-promise-constructor> |