Promise

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.

Das Promise-Objekt repräsentiert den zukünftigen Abschluss (oder das Scheitern) einer asynchronen Operation und deren resultierenden Wert.

Um zu erfahren, wie Promises funktionieren und wie Sie sie nutzen können, empfehlen wir Ihnen, zuerst Using promises zu lesen.

Beschreibung

Ein Promise ist ein Stellvertreter für einen Wert, der möglicherweise nicht bekannt ist, wenn das Promise erstellt wird. Es ermöglicht Ihnen, Handler mit dem letztendlichen Erfolg oder dem Fehlschlag eines asynchronen Vorgangs zu verknüpfen. Dadurch können asynchrone Methoden Werte wie synchrone Methoden zurückgeben: Statt sofort den endgültigen Wert zurückzugeben, gibt die asynchrone Methode ein Promise zurück, um den Wert zu einem späteren Zeitpunkt bereitzustellen.

Ein Promise befindet sich in einem dieser Zustände:

  • pending: Anfangszustand, weder erfüllt noch abgelehnt.
  • fulfilled: bedeutet, dass die Operation erfolgreich abgeschlossen wurde.
  • rejected: bedeutet, dass die Operation fehlgeschlagen ist.

Der endgültige Zustand eines ausstehenden Promise kann entweder mit einem Wert erfüllt oder mit einem Grund (Fehler) abgelehnt werden. Wenn eine dieser Optionen eintritt, werden die in der Warteschlange befindlichen Handler, die durch die then-Methode eines Promise zugeordnet sind, aufgerufen. Wenn das Promise bereits erfüllt oder abgelehnt wurde, wenn ein entsprechender Handler angehängt wird, wird der Handler aufgerufen, sodass es keine Race-Condition gibt zwischen dem Abschluss eines asynchronen Vorgangs und dem Anhängen seiner Handler.

Ein Promise wird als abgeschlossen bezeichnet, wenn es entweder erfüllt oder abgelehnt ist, aber nicht ausstehend.

Flussdiagramm, das zeigt, wie der Promise-Zustand zwischen ausstehend, erfüllt und abgelehnt über then/catch-Handler wechselt. Ein ausstehendes Promise kann entweder erfüllt oder abgelehnt werden. Wenn erfüllt, wird der "on fulfillment"-Handler oder der erste Parameter der then()-Methode ausgeführt und weitere asynchrone Aktionen werden durchgeführt. Wenn abgelehnt, wird der Fehler-Handler, entweder als zweiter Parameter der then()-Methode oder als einziger Parameter der catch()-Methode übergeben, ausgeführt.

Sie werden auch den Begriff resolved in Verbindung mit Promises hören — das bedeutet, dass das Promise abgeschlossen oder "festgelegt" ist, um den endgültigen Zustand eines anderen Promise zu entsprechen, und ein weiteres Erfüllen oder Ablehnen hat keine Wirkung. Das Dokument States and fates aus dem ursprünglichen Promise-Vorschlag enthält weitere Details zur Promise-Terminologie. Umgangssprachlich sind "resolved" Promises oft gleichbedeutend mit "fulfilled" Promises, aber wie in "States and fates" gezeigt, können resolved Promises auch ausstehend oder abgelehnt sein. Beispielsweise:

js
new Promise((resolveOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(resolveInner, 1000);
    }),
  );
});

Dieses Promise ist bereits resolved, wenn es erstellt wird (weil resolveOuter synchron aufgerufen wird), aber es wird mit einem anderen Promise aufgelöst und daher erst 1 Sekunde später erfüllt, wenn das innere Promise erfüllt wird. In der Praxis wird die "Auflösung" oft im Hintergrund und nicht beobachtbar durchgeführt, und nur ihre Erfüllung oder Ablehnung ist ersichtlich.

Hinweis: Mehrere andere Sprachen haben Mechanismen für faule Auswertung und das Verschieben einer Berechnung, die sie ebenfalls "promises" nennen, z.B. Scheme. Promises in JavaScript repräsentieren Prozesse, die bereits stattfinden, und die mit Callback-Funktionen verkettet werden können. Wenn Sie einen Ausdruck faul auswerten möchten, erwägen Sie die Verwendung einer Funktion ohne Argumente, z.B. f = () => expression, um den faul ausgewerteten Ausdruck zu erstellen, und f(), um den Ausdruck sofort auszuwerten.

Promise selbst hat kein erstklassiges Protokoll für die Stornierung, aber Sie können möglicherweise die zugrunde liegende asynchrone Operation direkt stornieren, typischerweise mithilfe von AbortController.

Verkettete Promises

Die Promise-Methoden then(), catch() und finally() werden verwendet, um eine weitere Aktion mit einem Promise zu verknüpfen, das abgeschlossen wird. Die then()-Methode nimmt bis zu zwei Argumente: das erste Argument ist eine Callback-Funktion für den Erfüllungsfall des Promise, und das zweite Argument ist eine Callback-Funktion für den Ablehnungsfall. Die catch() und finally()-Methoden rufen intern then() auf und machen die Fehlerbehandlung weniger verbos. Zum Beispiel ist ein catch() im Grunde nur ein then() ohne Übergabe des Erfüllungs-Handlers. Da diese Methoden Promises zurückgeben, können sie verkettet werden. Zum Beispiel:

js
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 300);
});

myPromise
  .then(handleFulfilledA, handleRejectedA)
  .then(handleFulfilledB, handleRejectedB)
  .then(handleFulfilledC, handleRejectedC);

Wir werden die folgende Terminologie verwenden: ursprüngliches Promise ist das Promise, auf dem then aufgerufen wird; neues Promise ist das Promise, das von then zurückgegeben wird. Die beiden an then übergebenen Callbacks werden Erfüllungshandler und Ablehnungshandler genannt.

Der abgeschlossene Zustand des ursprünglichen Promise bestimmt, welchen Handler auszuführen ist.

  • Wenn das ursprüngliche Promise erfüllt ist, wird der Erfüllungshandler mit dem Erfüllungswert aufgerufen.
  • Wenn das ursprüngliche Promise abgelehnt ist, wird der Ablehnungshandler mit dem Ablehnungsgrund aufgerufen.

Der Abschluss des Handlers bestimmt den abgeschlossenen Zustand des neuen Promise.

  • Wenn der Handler einen thenable Wert zurückgibt, wird das neue Promise in demselben Zustand wie der zurückgegebene Wert abgeschlossen.
  • Wenn der Handler einen nicht-thenable Wert zurückgibt, wird das neue Promise mit dem zurückgegebenen Wert erfüllt.
  • Wenn der Handler einen Fehler wirft, wird das neue Promise mit dem geworfenen Fehler abgelehnt.
  • Wenn das ursprüngliche Promise keinen entsprechenden Handler angehängt hat, wird das neue Promise in demselben Zustand wie das ursprüngliche Promise abgeschlossen — das heißt, ohne Ablehnungshandler bleibt ein abgelehntes Promise mit demselben Grund abgelehnt.

Zum Beispiel, im obigen Code, wenn myPromise ablehnt, wird handleRejectedA aufgerufen, und wenn handleRejectedA normal abschließt (ohne zu werfen oder ein abgelehntes Promise zurückzugeben), wird das von then zurückgegebene Promise erfüllt, anstatt abgelehnt zu bleiben. Daher, wenn ein Fehler sofort behandelt werden muss, aber der Fehlerzustand in der Kette beibehalten werden soll, müssen wir im Ablehnungshandler einen Fehler werfen. Andernfalls können wir die Fehlerbehandlung bis zum finalen catch()-Handler aufschieben, wenn kein unmittelbarer Bedarf besteht.

js
myPromise
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny);

Mit Pfeilfunktionen für die Callback-Funktionen könnte die Implementierung der Promise-Kette ungefähr so aussehen:

js
myPromise
  .then((value) => `${value} and bar`)
  .then((value) => `${value} and bar again`)
  .then((value) => `${value} and again`)
  .then((value) => `${value} and again`)
  .then((value) => {
    console.log(value);
  })
  .catch((err) => {
    console.error(err);
  });

Hinweis: Für eine schnellere Ausführung sollten nach Möglichkeit alle synchronen Aktionen innerhalb eines Handlers durchgeführt werden, andernfalls würde es mehrere Abläufe dauern, bis alle Handler sequentiell ausgeführt werden.

JavaScript unterhält eine Job-Warteschlange. Jedes Mal wählt JavaScript einen Job aus der Warteschlange aus und führt ihn vollständig aus. Die Jobs werden vom Executor des Promise()-Konstruktors, den an then übergebenen Handlern oder einer beliebigen Plattform-API, die ein Promise zurückgibt, definiert. Die Promises in einer Kette repräsentieren die Abhängigkeitsbeziehung zwischen diesen Jobs. Wenn ein Promise abgeschlossen wird, werden die jeweiligen damit verbundenen Handler am Ende der Job-Warteschlange hinzugefügt.

Ein Promise kann an mehr als einer Kette teilnehmen. In dem folgenden Code führt die Erfüllung von promiseA dazu, dass sowohl handleFulfilled1 als auch handleFulfilled2 zur Job-Warteschlange hinzugefügt werden. Da handleFulfilled1 zuerst registriert wird, wird es zuerst aufgerufen.

js
const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);

Eine Aktion kann einem bereits abgeschlossenen Promise zugewiesen werden. In diesem Fall wird die Aktion sofort an das Ende der Job-Warteschlange hinzugefügt und ausgeführt, wenn alle vorhandenen Jobs abgeschlossen sind. Daher wird eine Aktion für ein bereits "abgeschlossenes" Promise erst ausgeführt, nachdem der aktuelle synchrone Code abgeschlossen ist und mindestens ein Schleifen-Tick vergangen ist. Dies garantiert, dass Promise-Aktionen asynchron sind.

js
const promiseA = new Promise((resolve, reject) => {
  resolve(777);
});
// At this point, "promiseA" is already settled.
promiseA.then((val) => console.log("asynchronous logging has val:", val));
console.log("immediate logging");

// produces output in this order:
// immediate logging
// asynchronous logging has val: 777

Thenables

Das JavaScript-Ökosystem hatte viele Promise-Implementierungen lange bevor es Teil der Sprache wurde. Trotz unterschiedlicher interner Darstellung implementieren mindestens alle Promise-ähnlichen Objekte das Thenable-Interface. Ein Thenable implementiert die .then() Methode, die mit zwei Callbacks aufgerufen wird: einem für den Fall, dass das Promise erfüllt wird, und einem für den Fall, dass es abgelehnt wird. Promises sind auch Thenables.

Um mit bestehenden Promise-Implementierungen interoperieren zu können, erlaubt die Sprache die Verwendung von Thenables anstelle von Promises. Zum Beispiel, Promise.resolve wird nicht nur Promises auflösen, sondern auch Thenables nachverfolgen.

js
const aThenable = {
  then(onFulfilled, onRejected) {
    onFulfilled({
      // The thenable is fulfilled with another thenable
      then(onFulfilled, onRejected) {
        onFulfilled(42);
      },
    });
  },
};

Promise.resolve(aThenable); // A promise fulfilled with 42

Promise-Konkurrenz

Die Promise-Klasse bietet vier statische Methoden zur Erleichterung von asynchronen Aufgaben Konkurrenz:

Promise.all()

Erfüllt, wenn alle Promises erfüllt werden; lehnt ab, wenn irgendeines der Promises abgelehnt wird.

Promise.allSettled()

Erfüllt, wenn alle Promises abgeschlossen sind.

Promise.any()

Erfüllt, wenn eines der Promises erfüllt wird; lehnt ab, wenn alle der Promises abgelehnt werden.

Promise.race()

Wird abgeschlossen, wenn irgendeines der Promises abgeschlossen ist. Mit anderen Worten, erfüllt, wenn eines der Promises erfüllt wird; lehnt ab, wenn eines der Promises abgelehnt wird.

All diese Methoden nehmen ein iterable von Promises (genauer gesagt Thenables) und geben ein neues Promise zurück. Sie unterstützen alle das Subclassing, was bedeutet, dass sie auf Subklassen von Promise aufgerufen werden können und das Ergebnis ein Promise des Subklassen-Typs sein wird. Dazu muss der Konstruktor der Subklasse dieselbe Signatur wie der Promise()-Konstruktor implementieren — er muss eine einzelne executor-Funktion akzeptieren, die mit den resolve- und reject-Callbacks als Parameter aufgerufen werden kann. Die Subklasse muss auch eine resolve-statische Methode haben, die wie Promise.resolve() aufgerufen werden kann, um Werte in Promises umzuwandeln.

Beachten Sie, dass JavaScript von Natur aus eingleisig ist, sodass zu einem bestimmten Zeitpunkt nur eine Aufgabe ausgeführt wird, obwohl die Kontrolle zwischen verschiedenen Promises wechseln kann, wodurch die Ausführung der Promises gleichzeitig erscheint. Parallelausführung in JavaScript kann nur durch Worker-Threads erreicht werden.

Konstruktor

Promise()

Erstellt ein neues Promise-Objekt. Der Konstruktor wird hauptsächlich verwendet, um Funktionen zu ummanteln, die Promises nicht bereits unterstützen.

Statische Eigenschaften

Promise[Symbol.species]

Gibt den Konstruktor zurück, der verwendet wird, um Rückgabewerte von Promise-Methoden zu konstruieren.

Statische Methoden

Promise.all()

Nimmt ein Iterable von Promises als Eingabe und gibt ein einzelnes Promise zurück. Dieses zurückgegebene Promise wird erfüllt, wenn alle Promises des Inputs (einschließlich des Falles, wenn ein leeres Iterable übergeben wird) erfüllt werden, mit einem Array der Erfüllungswerte. Es lehnt ab, wenn eines der Eingabe-Promises mit dem ersten Grund der Ablehnung ablehnt.

Promise.allSettled()

Nimmt ein Iterable von Promises als Eingabe und gibt ein einzelnes Promise zurück. Dieses zurückgegebene Promise wird erfüllt, wenn alle Promises des Inputs (einschließlich des Falles, wenn ein leeres Iterable übergeben wird) abgeschlossen werden, mit einem Array von Objekten, die das Ergebnis jedes Promise beschreiben.

Promise.any()

Nimmt ein Iterable von Promises als Eingabe und gibt ein einzelnes Promise zurück. Dieses zurückgegebene Promise wird erfüllt, wenn eines der Promises des Inputs erfüllt wird, mit diesem ersten Erfüllungswert. Es lehnt ab, wenn alle Promises des Inputs (einschließlich des Falles, wenn ein leeres Iterable übergeben wird) ablehnen, mit einem AggregateError, der ein Array von Ablehnungsgründen enthält.

Promise.race()

Nimmt ein Iterable von Promises als Eingabe und gibt ein einzelnes Promise zurück. Dieses zurückgegebene Promise wird mit dem endgültigen Zustand des ersten Promise abgeschlossen, das abgeschlossen wird.

Promise.reject()

Gibt ein neues Promise-Objekt zurück, das mit dem angegebenen Grund abgelehnt wird.

Promise.resolve()

Gibt ein Promise-Objekt zurück, das mit dem angegebenen Wert aufgelöst wird. Wenn der Wert ein Thenable ist (d.h. eine then-Methode hat), wird das zurückgegebene Promise diesem Thenable "folgen" und seinen endgültigen Zustand übernehmen; ansonsten wird das zurückgegebene Promise mit dem Wert erfüllt.

Promise.try()

Nimmt einen Callback beliebiger Art (gibt zurück oder wirft, synchron oder asynchron) und ummantelt dessen Ergebnis in einem Promise.

Promise.withResolvers()

Gibt ein Objekt zurück, das ein neues Promise-Objekt und zwei Funktionen zum Auflösen oder Ablehnen des Promise enthält, entsprechend den zwei Parametern, die an den Executor des Promise()-Konstruktors übergeben werden.

Instanz-Eigenschaften

Diese Eigenschaften sind auf Promise.prototype definiert und werden von allen Promise-Instanzen geteilt.

Promise.prototype.constructor

Die Konstruktorfunktion, die das Instanzobjekt erstellt hat. Für Promise-Instanzen ist der ursprüngliche Wert der Promise Konstruktor.

Promise.prototype[Symbol.toStringTag]

Der anfängliche Wert der [Symbol.toStringTag]-Eigenschaft ist die Zeichenkette "Promise". Diese Eigenschaft wird in Object.prototype.toString() verwendet.

Instanz-Methoden

Promise.prototype.catch()

Hängt einen Ablehnungshandler-Callback an das Promise an und gibt ein neues Promise zurück, das auf den Rückgabewert des Callbacks aufgelöst wird, wenn es aufgerufen wird, oder auf seinen ursprünglichen Erfüllungswert, wenn das Promise stattdessen erfüllt wird.

Promise.prototype.finally()

Hängt einen Handler an das Promise an und gibt ein neues Promise zurück, das aufgelöst wird, wenn das ursprüngliche Promise aufgelöst wird. Der Handler wird aufgerufen, wenn das Promise abgeschlossen wird, ob erfüllt oder abgelehnt.

Promise.prototype.then()

Hängt Erfüllungs- und Ablehnungshandler an das Promise an und gibt ein neues Promise zurück, das auf den Rückgabewert des aufgerufenen Handlers aufgelöst wird, oder auf dessen ursprünglichen abgeschlossenen Wert, wenn das Promise nicht behandelt wurde (d.h. wenn der relevante Handler onFulfilled oder onRejected keine Funktion ist).

Beispiele

Einfaches Beispiel

In diesem Beispiel verwenden wir setTimeout(...) um asynchronen Code zu simulieren. In der Realität werden Sie wahrscheinlich etwas wie XHR oder eine HTML-API verwenden.

js
const myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously
  // was successful, and reject(...) when it failed.
  setTimeout(() => {
    resolve("Success!"); // Yay! Everything went well!
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log(`Yay! ${successMessage}`);
});

Beispiel mit verschiedenen Situationen

Dieses Beispiel zeigt verschiedene Techniken zur Nutzung von Promise-Funktionalitäten und diverse Situationen, die auftreten können. Um dies zu verstehen, beginnen Sie am besten, den Promise-Chain unten im Codeblock zu betrachten. Nach der Bereitstellung eines anfänglichen Promise kann eine Kette von Promises folgen. Die Kette besteht aus .then()-Aufrufen und hat typischerweise (aber nicht unbedingt) ein einzelnes .catch() am Ende, gefolgt von .finally(). In diesem Beispiel wird die Promise-Kette durch ein selbstgeschriebenes new Promise()-Konstrukt initiiert; in der Praxis beginnen Promise-Ketten normalerweise mit einer API-Funktion (von jemand anderem geschrieben), die ein Promise zurückgibt.

Die Beispielfunktion tetheredGetNumber() zeigt, dass ein Promise-Generator reject() sowohl während der Einrichtung eines asynchronen Aufrufs als auch innerhalb des Callbacks verwenden kann, oder beides. Die Funktion promiseGetWord() veranschaulicht, wie eine API-Funktion ein Promise auf eine selbstständige Weise generieren und zurückgeben könnte.

Beachten Sie, dass die Funktion troubleWithGetNumber() mit einem throw endet. Das ist erzwungen, weil eine Promise-Kette durch alle .then() Promises geht, selbst nach einem Fehler, und ohne throw würde der Fehler als "behoben" erscheinen. Das ist lästig und deshalb ist es üblich, onRejected in der gesamten Kette der .then() Promises auszulassen und nur ein einziges onRejected im finalen catch() zu haben.

Dieser Code kann unter NodeJS ausgeführt werden. Das Verständnis wird durch das tatsächliche Eintreten der Fehler erleichtert. Um mehr Fehler zu erzwingen, ändern Sie die threshold-Werte.

js
// To experiment with error handling, "threshold" values cause errors randomly
const THRESHOLD_A = 8; // can use zero 0 to guarantee error

function tetheredGetNumber(resolve, reject) {
  setTimeout(() => {
    const randomInt = Date.now();
    const value = randomInt % 10;
    if (value < THRESHOLD_A) {
      resolve(value);
    } else {
      reject(`Too large: ${value}`);
    }
  }, 500);
}

function determineParity(value) {
  const isOdd = value % 2 === 1;
  return { value, isOdd };
}

function troubleWithGetNumber(reason) {
  const err = new Error("Trouble getting number", { cause: reason });
  console.error(err);
  throw err;
}

function promiseGetWord(parityInfo) {
  return new Promise((resolve, reject) => {
    const { value, isOdd } = parityInfo;
    if (value >= THRESHOLD_A - 1) {
      reject(`Still too large: ${value}`);
    } else {
      parityInfo.wordEvenOdd = isOdd ? "odd" : "even";
      resolve(parityInfo);
    }
  });
}

new Promise(tetheredGetNumber)
  .then(determineParity, troubleWithGetNumber)
  .then(promiseGetWord)
  .then((info) => {
    console.log(`Got: ${info.value}, ${info.wordEvenOdd}`);
    return info;
  })
  .catch((reason) => {
    if (reason.cause) {
      console.error("Had previously handled error");
    } else {
      console.error(`Trouble with promiseGetWord(): ${reason}`);
    }
  })
  .finally((info) => console.log("All done"));

Fortgeschrittenes Beispiel

Dieses kleine Beispiel zeigt den Mechanismus eines Promise. Die testPromise()-Methode wird jedes Mal aufgerufen, wenn der <button> geklickt wird. Sie erstellt ein Promise, das erfüllt wird, indem es mithilfe von setTimeout() mit der Promise-Anzahl (beginnend bei 1) alle 1-3 Sekunden zufällig aufgelöst wird. Der Promise()-Konstruktor wird verwendet, um das Promise zu erstellen.

Das Erfüllen des Promise wird protokolliert, über einen Erfüllungs-Callback, der mit p1.then() gesetzt wird. Ein paar Protokolle zeigen, wie der synchrone Teil der Methode vom asynchronen Abschluss des Promise entkoppelt ist.

Durch mehrmaliges Klicken auf den Button in kurzer Zeit sehen Sie sogar, wie die verschiedenen Promises nacheinander erfüllt werden.

HTML

html
<button id="make-promise">Make a promise!</button>
<div id="log"></div>

JavaScript

js
"use strict";

let promiseCount = 0;

function testPromise() {
  const thisPromiseCount = ++promiseCount;
  const log = document.getElementById("log");
  // begin
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Started<br>`);
  // We make a new promise: we promise a numeric count of this promise,
  // starting from 1 (after waiting 3s)
  const p1 = new Promise((resolve, reject) => {
    // The executor function is called with the ability
    // to resolve or reject the promise
    log.insertAdjacentHTML(
      "beforeend",
      `${thisPromiseCount}) Promise constructor<br>`,
    );
    // This is only an example to create asynchronism
    setTimeout(
      () => {
        // We fulfill the promise
        resolve(thisPromiseCount);
      },
      Math.random() * 2000 + 1000,
    );
  });

  // We define what to do when the promise is resolved with the then() call,
  // and what to do when the promise is rejected with the catch() call
  p1.then((val) => {
    // Log the fulfillment value
    log.insertAdjacentHTML("beforeend", `${val}) Promise fulfilled<br>`);
  }).catch((reason) => {
    // Log the rejection reason
    console.log(`Handle rejected promise (${reason}) here.`);
  });
  // end
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Promise made<br>`);
}

const btn = document.getElementById("make-promise");
btn.addEventListener("click", testPromise);

Ergebnis

Laden eines Bildes mit XHR

Ein weiteres Beispiel, das Promise und XMLHttpRequest verwendet, um ein Bild zu laden, wird unten gezeigt. Jeder Schritt ist kommentiert, um die Promise- und XHR-Architektur genau zu verfolgen.

js
function imgLoad(url) {
  // Create new promise with the Promise() constructor;
  // This has as its argument a function with two parameters, resolve and reject
  return new Promise((resolve, reject) => {
    // XHR to load an image
    const request = new XMLHttpRequest();
    request.open("GET", url);
    request.responseType = "blob";
    // When the request loads, check whether it was successful
    request.onload = () => {
      if (request.status === 200) {
        // If successful, resolve the promise by passing back the request response
        resolve(request.response);
      } else {
        // If it fails, reject the promise with an error message
        reject(
          Error(
            `Image didn't load successfully; error code: + ${request.statusText}`,
          ),
        );
      }
    };
    // Handle network errors
    request.onerror = () => reject(new Error("There was a network error."));
    // Send the request
    request.send();
  });
}

// Get a reference to the body element, and create a new image object
const body = document.querySelector("body");
const myImage = new Image();
const imgUrl =
  "https://mdn.github.io/shared-assets/images/examples/round-balloon.png";

// Call the function with the URL we want to load, then chain the
// promise then() method with two callbacks
imgLoad(imgUrl).then(
  (response) => {
    // The first runs when the promise resolves, with the request.response
    // specified within the resolve() method.
    const imageURL = URL.createObjectURL(response);
    myImage.src = imageURL;
    body.appendChild(myImage);
  },
  (error) => {
    // The second runs when the promise
    // is rejected, and logs the Error specified with the reject() method.
    console.log(error);
  },
);

Nachverfolgung des aktiven Einstellungsobjekts

Ein Einstellungsobjekt ist eine Umgebung, die zusätzliche Informationen bereitstellt, wenn JavaScript-Code ausgeführt wird. Dies umfasst das Reich und die Modulkarte sowie HTML-spezifische Informationen wie den Ursprung. Das aktive Einstellungsobjekt wird verfolgt, um sicherzustellen, dass der Browser weiß, welches für ein bestimmtes Stück Benutzer-Code verwendet werden soll.

Um dies besser zu veranschaulichen, können wir uns genauer ansehen, wie das Reich ein Problem sein könnte. Ein Reich kann grob als das globale Objekt verstanden werden. Was Reiche einzigartig macht, ist, dass sie alle notwendigen Informationen zum Ausführen von JavaScript-Code enthalten. Dazu gehören Objekte wie Array und Error. Jedes Einstellungsobjekt hat seine eigene "Kopie" dieser Objekte und sie werden nicht geteilt. Das kann einige unerwartete Verhaltensweisen in Bezug auf Promises verursachen. Um dies zu umgehen, verfolgen wir etwas, das als aktives Einstellungsobjekt bezeichnet wird. Dies stellt Informationen dar, die spezifisch für den Kontext des Benutzer-Codes sind, der für einen bestimmten Funktionsaufruf verantwortlich ist.

Um dies noch weiter zu verdeutlichen, können wir uns ansehen, wie ein in ein Dokument eingebettetes <iframe> mit seinem Host kommuniziert. Da alle Web-APIs sich des aktiven Einstellungsobjekts bewusst sind, funktioniert Folgendes in allen Browsern:

html
<!doctype html> <iframe></iframe>
<!-- we have a realm here -->
<script>
  // we have a realm here as well
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  // bound is a built-in function — there is no user
  // code on the stack, so which realm do we use?
  setTimeout(bound);
  // this still works, because we use the youngest
  // realm (the incumbent) on the stack
</script>

Dasselbe Konzept gilt für Promises. Wenn wir das obige Beispiel ein wenig modifizieren, erhalten wir dies:

html
<!doctype html> <iframe></iframe>
<!-- we have a realm here -->
<script>
  // we have a realm here as well
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  // bound is a built in function — there is no user
  // code on the stack — which realm do we use?
  Promise.resolve(undefined).then(bound);
  // this still works, because we use the youngest
  // realm (the incumbent) on the stack
</script>

Wenn wir dies ändern, sodass das <iframe> im Dokument auf Postnachrichten hört, können wir den Effekt des aktiven Einstellungsobjekts beobachten:

html
<!-- y.html -->
<!doctype html>
<iframe src="x.html"></iframe>
<script>
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  Promise.resolve(undefined).then(bound);
</script>
html
<!-- x.html -->
<!doctype html>
<script>
  window.addEventListener(
    "message",
    (event) => {
      document.querySelector("#text").textContent = "hello";
      // this code will only run in browsers that track the incumbent settings object
      console.log(event);
    },
    false,
  );
</script>

Im obigen Beispiel wird der innere Text des <iframe> nur aktualisiert, wenn das aktive Einstellungsobjekt verfolgt wird. Dies liegt daran, dass ohne die Nachverfolgung des aktiven Objekts möglicherweise die falsche Umgebung verwendet wird, um die Nachricht zu senden.

Hinweis: Derzeit ist die Nachverfolgung des aktiven Reichs in Firefox vollständig implementiert und hat teilweise Implementierungen in Chrome und Safari.

Spezifikationen

Specification
ECMAScript Language Specification
# sec-promise-objects

Browser-Kompatibilität

BCD tables only load in the browser

Siehe auch