Promise.race()
Базовый уровень Широко доступен
Эта функция хорошо зарекомендовала себя и работает на многих устройствах и в различных версиях браузеров. Она доступна во всех браузерах с июля 2015 года.
Статический метод Promise.race() принимает в качестве входных данных итерируемый объект промисов и возвращает единственный Promise. Этот возвращенный промис разрешается с окончательным состоянием первого промиса, который разрешается.
Попробовать
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "one");
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "two");
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster
});
// Expected output: "two"
Синтаксис
Promise.race(iterable)
Параметры
-
iterable - Итерируемый объект (например,
Array) промисов. Эти значения ожидаются, поэтому другие thenable-объекты также разрешаются, в то время как не-thenable-объекты возвращаются как есть.
Возвращаемое значение
Promise, который асинхронно разрешается с окончательным состоянием первого промиса в iterable, который разрешится. Другими словами, он выполняется (fulfill), если первый разрешившийся промис выполняется, и отклоняется (reject), если первый разрешившийся промис отклоняется. Возвращаемый промис остается в состоянии ожидания (pending) навсегда, если переданный iterable пуст. Если переданный iterable не пуст, но не содержит ожидающих промисов, возвращаемый промис все равно разрешается асинхронно (вместо синхронного разрешения).
Описание
Метод Promise.race() является одним из методов параллелизма промисов. Он полезен, когда вы хотите, чтобы первая асинхронная задача завершилась, но вас не волнует ее окончательное состояние (т. е. она может как выполниться, так и завершиться с ошибкой).
Если итерируемый объект содержит одно или несколько значений, не являющихся промисами, и/или уже разрешенный промис, то Promise.race() разрешится до первого из этих значений, найденного в итерируемом объекте.
Как и другие комбинаторы промисов, Promise.race() немедленно помечает все промисы как «обработанные» при вызове (путем вызова их методов .then()). Последующие отклонения после первого разрешения будут игнорироваться и не вызовут никаких событий unhandledrejection.
Разрешение возвращенного промиса не отменяет проигравшие операции и не отписывает обработчики, прикрепленные к их промисам. Если вы многократно устраиваете гонку между долгоживущим ожидающим промисом и быстро завершающимися промисами, обработчики могут накапливаться на ожидающем промисе даже после разрешения каждой гонки.
Примеры
Использование Promise.race()
Этот пример показывает, как можно использовать Promise.race() для организации гонки нескольких таймеров, реализованных с помощью setTimeout(). Таймер с наименьшим временем всегда выигрывает гонку и становится состоянием результирующего промиса.
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"
// Both fulfill, but p2 is faster
});
const p3 = sleep(100, "three", "fulfill");
const p4 = sleep(500, "four", "reject");
Promise.race([p3, p4]).then(
(value) => {
console.log(value); // "three"
// p3 is faster, so it fulfills
},
(error) => {
// Not called
},
);
const p5 = sleep(500, "five", "fulfill");
const p6 = sleep(100, "six", "reject");
Promise.race([p5, p6]).then(
(value) => {
// Not called
},
(error) => {
console.error(error.message); // "six"
// p6 is faster, so it rejects
},
);
Асинхронность Promise.race
Следующий пример демонстрирует асинхронность Promise.race. В отличие от других методов параллелизма промисов, Promise.race всегда асинхронен: он никогда не разрешается синхронно, даже когда iterable пуст.
// Passing an array of promises that are already resolved,
// to trigger Promise.race as soon as possible
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];
const p = Promise.race(resolvedPromisesArray);
// Immediately logging the value of p
console.log(p);
// Using setTimeout, we can execute code after the stack is empty
setTimeout(() => {
console.log("the stack is now empty");
console.log(p);
});
// Logs, in order:
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: 33 }
Пустой итерируемый объект приводит к тому, что возвращаемый промис навсегда остается в состоянии ожидания (pending):
const foreverPendingPromise = Promise.race([]);
console.log(foreverPendingPromise);
setTimeout(() => {
console.log("the stack is now empty");
console.log(foreverPendingPromise);
});
// Logs, in order:
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "pending" }
Если итерируемый объект содержит одно или несколько значений, не являющихся промисами, и/или уже разрешенный промис, то Promise.race разрешится до первого из этих значений, найденного в массиве:
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);
});
// Logs, in order:
// Promise { <state>: "pending" }
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: 100 }
// Promise { <state>: "fulfilled", <value>: "non-Promise value" }
Использование Promise.race() для реализации тайм-аута запроса
Вы можете устроить гонку потенциально длительного запроса с таймером, который отклоняется, чтобы по истечении лимита времени результирующий промис автоматически отклонился.
const data = Promise.race([
fetch("/api"),
new Promise((resolve, reject) => {
// Reject after 5 seconds
setTimeout(() => reject(new Error("Request timed out")), 5000);
}),
])
.then((res) => res.json())
.catch((err) => displayError(err));
Если промис data выполняется, он будет содержать данные, полученные из /api. Promise.race захватит и отбросит результаты разрешения проигравших промисов, поэтому отклонение "Request timed out" не всплывет как необработанное. В противном случае, если fetch остается в состоянии ожидания в течение 5 секунд и проигрывает гонку таймеру setTimeout, финальный промис будет отклонен.
Завершение одного промиса не отменяет автоматически другой; результат другого просто игнорируется. Это не создает проблем в этом небольшом примере, но оставляет ресурсы, такие как сетевые соединения и таймеры, активными дольше, чем необходимо. Чтобы освободить ресурсы раньше, прервите fetch, если тайм-аут выигрывает, или отмените тайм-аут, если выигрывает fetch. Всякий раз, когда это возможно (включая fetch), вместо этого используйте API AbortController.
Использование Promise.race() для определения статуса промиса
Поскольку Promise.race() разрешается до первого не-ожидающего промиса в итерируемом объекте, мы можем проверить состояние промиса, в том числе, находится ли он в состоянии ожидания. Этот пример адаптирован из promise-status-async.
function promiseState(promise) {
const pendingState = { status: "pending" };
return Promise.race([promise, pendingState]).then(
(value) =>
value === pendingState ? value : { status: "fulfilled", value },
(reason) => ({ status: "rejected", reason }),
);
}
В этой функции, если promise находится в состоянии ожидания (pending), второе значение, pendingState, которое не является промисом, становится результатом гонки; в противном случае, если promise уже разрешен, мы можем узнать его состояние через обработчики onFulfilled и onRejected. Например:
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);
// Logs:
// Immediately after initiation:
// { status: 'pending' }
// { status: 'pending' }
// { status: 'pending' }
// After waiting for 100ms:
// { status: 'fulfilled', value: 100 }
// { status: 'pending' }
// { status: 'rejected', reason: Error: failed }
Примечание: Функция promiseState по-прежнему выполняется асинхронно, поскольку невозможно синхронно получить значение промиса (т. е. без then() или await), даже если он уже разрешен. Однако promiseState() всегда выполняется в течение одного "тика" и никогда фактически не ждет разрешения какого-либо промиса.
Сравнение с Promise.any()
Promise.race принимает первый разрешившийся Promise.
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) => {
// Only promise1 is fulfilled, but promise2 is faster
console.error("failed with reason:", reason);
});
// failed with reason: two
Promise.any принимает первый выполненный Promise.
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) => {
// Only promise1 is fulfilled, even though promise2 settled sooner
console.log("succeeded with value:", value);
})
.catch((reason) => {
console.error("failed with reason:", reason);
});
// succeeded with value: one
Спецификации
Совместимость с браузерами
| Десктоп | Мобильные | Сервер | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari on iOS | Samsung Internet | WebView Android | WebView on iOS | Bun | Deno | Node.js | |
race |
32 |
12 |
29 |
19 |
8 |
32 |
29 |
19 |
8 |
2.0 |
4.4.3 |
8 |
1.0.0 |
1.0 |
0.12.0 |
Смотрите также
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race