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

Promise.race()

Methode

Die statische Methode <code>Promise.race()</code> nimmt ein Iterable von Promises entgegen und liefert ein Promise, das mit dem Zustand des zuerst abgeschlossenen Promises settled.

Kategorie: method-property

Signatur

Promise.race(iterable)

Beschreibung

Die statische Methode Promise.race() nimmt ein Iterable von Promises als Eingabe entgegen und gibt ein einzelnes Promise zurück. Dieses zurückgegebene Promise settled mit dem endgültigen Zustand des ersten Promises, das settled.

Die Methode Promise.race() ist eine der Methoden für Promise-Nebenläufigkeit. Sie ist nützlich, wenn du willst, dass die erste asynchrone Aufgabe abgeschlossen wird, dir der endgültige Zustand aber egal ist (d.h. sie kann entweder erfolgreich sein oder fehlschlagen).

Wenn das Iterable einen oder mehrere Nicht-Promise-Werte und/oder ein bereits abgeschlossenes Promise enthält, dann settled Promise.race() zum ersten dieser im Iterable gefundenen Werte.

Wie andere Promise-Kombinatoren markiert Promise.race() alle Promises sofort als „handled", wenn es aufgerufen wird (durch den Aufruf ihrer .then()-Methoden). Nachfolgende Rejections nach dem ersten Settlement werden ignoriert und lösen keine unhandledrejection-Events aus.

Das Settlen des zurückgegebenen Promises bricht die verlierenden Operationen nicht ab und meldet die an ihre Promises angehängten Handler nicht ab. Wenn du wiederholt ein langlebiges, ausstehendes Promise gegen kurzlebige Promises antreten lässt, können sich Handler am ausstehenden Promise auch nach jedem Race-Settlement anhäufen.

Parameter

Name Typ Default Beschreibung
$iterable Pflicht Iterable Ein Iterable (wie ein Array) von Promises. Diese Werte werden awaited, sodass andere thenables ebenfalls resolved werden, während Nicht-thenables unverändert zurückgegeben werden.

Rückgabewert

Typ
Promise
Beschreibung
Ein Promise, das asynchron settled mit dem endgültigen Zustand des ersten Promises im iterable, das settled. Mit anderen Worten: Es fulfillt, wenn das erste settled Promise fulfilled ist, und rejected, wenn das erste settled Promise rejected ist. Das zurückgegebene Promise bleibt für immer pending, wenn das übergebene iterable leer ist. Wenn das übergebene iterable nicht leer ist, aber keine pending Promises enthält, wird das zurückgegebene Promise trotzdem asynchron (statt synchron) settled.

Beispiele

Verwendung von Promise.race()

function sleep(time, value, state) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (state === "fulfill") {
        resolve(value);
      } else {
        reject(new Error(value));
      }
    }, time);
  });
}

const p1 = sleep(500, "one", "fulfill");
const p2 = sleep(100, "two", "fulfill");

Promise.race([p1, p2]).then((value) => {
  console.log(value); // "two"
  // Beide fulfillen, aber p2 ist schneller
});

const p3 = sleep(100, "three", "fulfill");
const p4 = sleep(500, "four", "reject");

Promise.race([p3, p4]).then(
  (value) => {
    console.log(value); // "three"
    // p3 ist schneller, also fulfillt es
  },
  (error) => {
    // Wird nicht aufgerufen
  },
);

const p5 = sleep(500, "five", "fulfill");
const p6 = sleep(100, "six", "reject");

Promise.race([p5, p6]).then(
  (value) => {
    // Wird nicht aufgerufen
  },
  (error) => {
    console.error(error.message); // "six"
    // p6 ist schneller, also rejected es
  },
);

Asynchronität von Promise.race

// Übergabe eines Arrays von bereits resolveden Promises,
// um Promise.race so schnell wie möglich auszulösen
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];

const p = Promise.race(resolvedPromisesArray);
// Sofortiges Loggen des Wertes von p
console.log(p);

// Mit setTimeout können wir Code ausführen, nachdem der Stack leer ist
setTimeout(() => {
  console.log("the stack is now empty");
  console.log(p);
});

// Loggt in dieser Reihenfolge:
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: 33 }

Leeres Iterable führt zu dauerhaft pending Promise

const foreverPendingPromise = Promise.race([]);
console.log(foreverPendingPromise);
setTimeout(() => {
  console.log("the stack is now empty");
  console.log(foreverPendingPromise);
});

// Loggt in dieser Reihenfolge:
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "pending" }

Iterable mit bereits settled Werten

const foreverPendingPromise = Promise.race([]);
const alreadyFulfilledProm = Promise.resolve(100);

const arr = [foreverPendingPromise, alreadyFulfilledProm, "non-Promise value"];
const arr2 = [foreverPendingPromise, "non-Promise value", Promise.resolve(100)];
const p = Promise.race(arr);
const p2 = Promise.race(arr2);

console.log(p);
console.log(p2);
setTimeout(() => {
  console.log("the stack is now empty");
  console.log(p);
  console.log(p2);
});

// Loggt in dieser Reihenfolge:
// Promise { <state>: "pending" }
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: 100 }
// Promise { <state>: "fulfilled", <value>: "non-Promise value" }

Verwendung von Promise.race() zur Implementierung eines Request-Timeouts

const data = Promise.race([
  fetch("/api"),
  new Promise((resolve, reject) => {
    // Reject nach 5 Sekunden
    setTimeout(() => reject(new Error("Request timed out")), 5000);
  }),
])
  .then((res) => res.json())
  .catch((err) => displayError(err));

Verwendung von Promise.race() zur Erkennung des Status eines Promises

function promiseState(promise) {
  const pendingState = { status: "pending" };

  return Promise.race([promise, pendingState]).then(
    (value) =>
      value === pendingState ? value : { status: "fulfilled", value },
    (reason) => ({ status: "rejected", reason }),
  );
}

Beispielhafte Anwendung von promiseState

const p1 = new Promise((res) => setTimeout(() => res(100), 100));
const p2 = new Promise((res) => setTimeout(() => res(200), 200));
const p3 = new Promise((res, rej) =>
  setTimeout(() => rej(new Error("failed")), 100),
);

async function getStates() {
  console.log(await promiseState(p1));
  console.log(await promiseState(p2));
  console.log(await promiseState(p3));
}

console.log("Immediately after initiation:");
getStates();
setTimeout(() => {
  console.log("After waiting for 100ms:");
  getStates();
}, 100);

// Loggt:
// Immediately after initiation:
// { status: 'pending' }
// { status: 'pending' }
// { status: 'pending' }
// After waiting for 100ms:
// { status: 'fulfilled', value: 100 }
// { status: 'pending' }
// { status: 'rejected', reason: Error: failed }

Vergleich mit Promise.any() — Promise.race

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "one");
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, "two");
});

Promise.race([promise1, promise2])
  .then((value) => {
    console.log("succeeded with value:", value);
  })
  .catch((reason) => {
    // Nur promise1 ist fulfilled, aber promise2 ist schneller
    console.error("failed with reason:", reason);
  });
// failed with reason: two

Vergleich mit Promise.any() — Promise.any

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "one");
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, "two");
});

Promise.any([promise1, promise2])
  .then((value) => {
    // Nur promise1 ist fulfilled, obwohl promise2 früher settled
    console.log("succeeded with value:", value);
  })
  .catch((reason) => {
    console.error("failed with reason:", reason);
  });
// succeeded with value: one

// Wichtig · Fallstricke

Die Funktion promiseState läuft weiterhin asynchron, da es keine Möglichkeit gibt, den Wert eines Promises synchron zu erhalten (d.h. ohne then() oder await), selbst wenn es bereits settled ist. promiseState() fulfillt jedoch immer innerhalb eines Ticks und wartet nie tatsächlich auf das Settlement eines Promises.