Несколько промисов сразу
Часто нужно выполнить несколько асинхронных действий одновременно: загрузить профиль и список заказов, опросить три сервера. Для этого у объекта Promise есть статические методы, которые принимают массив промисов и возвращают один общий промис. Их называют комбинаторами.
Во всех примерах урока используется маленькая функция wait(): она возвращает промис, который выполняется со значением value через ms миллисекунд.
Promise.all
Promise.all() ждёт, пока выполнятся все промисы, и отдаёт массив результатов в том же порядке, в каком были промисы:
const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
Promise.all([wait('a', 300), wait('b', 100)]).then((values) => {
console.log(values.join(', ')); // a, b
});Оба ожидания идут одновременно, поэтому всё вместе занимает 300 мс, а не 400.
Если один промис упал
Если хотя бы один промис отклонён, Promise.all() сразу отклоняется с его ошибкой, а остальные результаты теряются:
const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
Promise.all([wait(1, 100), Promise.reject(new Error('Запрос 2 не удался')), wait(3, 200)])
.then((values) => console.log(values.join(', ')))
.catch((error) => console.log(error.message)); // Запрос 2 не удалсяЭто удобно, когда без любого из результатов продолжать бессмысленно.
Promise.allSettled
Promise.allSettled() ждёт все промисы, даже отклонённые. Каждый результат — объект с полем status: 'fulfilled' и значением value или 'rejected' и ошибкой reason:
Promise.allSettled([Promise.resolve(10), Promise.reject(new Error('Нет связи'))]).then((results) => {
for (const result of results) {
console.log(result.status);
}
});
// fulfilled
// rejectedПодходит, когда нужно показать всё, что удалось загрузить, и отдельно — что не удалось.
Promise.race
Promise.race() завершается вместе с первым завершившимся промисом — неважно, успешно или с ошибкой. Так делают ограничение по времени:
const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
const timeout = (ms) => new Promise((resolve, reject) => setTimeout(() => reject(new Error('Время вышло')), ms));
Promise.race([wait('Ответ сервера', 500), timeout(200)])
.then((value) => console.log(value))
.catch((error) => console.log(error.message)); // Время вышлоPromise.any
Promise.any() ждёт первый успешный промис и не обращает внимания на ошибки. Отклоняется он, только если неудачны все:
const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
Promise.any([Promise.reject(new Error('Сервер 1 недоступен')), wait('Сервер 2', 300), wait('Сервер 3', 100)])
.then((value) => console.log(value)); // Сервер 3| Метод | Когда выполняется | Когда отклоняется |
|---|---|---|
Promise.all() | Все успешны | Первая же ошибка |
Promise.allSettled() | Все завершились | Никогда |
Promise.race() | Первый завершился успешно | Первый завершился ошибкой |
Promise.any() | Первый успешный | Все с ошибкой |
Частая ошибка
Передать промисы отдельными аргументами, а не массивом. Promise.all(first, second) не ждёт ни одного из них, а сразу отклоняется с ошибкой TypeError:
const first = Promise.resolve(1);
const second = Promise.resolve(2);
Promise.all(first, second).catch((error) => console.log(error.name)); // TypeErrorПравильно — квадратные скобки: Promise.all([first, second]).