Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Несколько промисов сразу

Часто нужно выполнить несколько асинхронных действий одновременно: загрузить профиль и список заказов, опросить три сервера. Для этого у объекта 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]).

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.