Promise.allSettled()

Запускаем несколько промисов параллельно и ждём, когда они все завершатся.

Время чтения: меньше 5 мин

Кратко

Скопировано

Метод allSettled() — это один из статических методов объекта Promise. Его используют, когда нужно запустить несколько промисов параллельно и дождаться их выполнения.

Promise.allSettled() очень похож на метод Promise.all(), но работает немного по-другому. В отличие от Promise.all(), Promise.allSettled() ждёт выполнения всех промисов, при этом неважно, завершились они успешно или с ошибкой.

Как пишется

Скопировано

Promise.allSettled() принимает итерируемую коллекцию промисов (чаще всего — массив) и возвращает новый промис, который будет выполнен, когда будут выполнены все переданные промисы. Полученный промис содержит массив результатов выполнения всех переданных промисов, сохраняя порядок оригинального массива, но не порядок выполнения.

Как понять

Скопировано

Создадим массив промисов и передадим его в Promise.allSettled(). Один из созданных промисов завершится ошибкой:

        
          
          const promises = [  new Promise(resolve => setTimeout(() => resolve(1), 3000)),  new Promise((resolve, reject) => setTimeout(() => reject('error'), 2000)),  new Promise(resolve => setTimeout(() => resolve(3), 1000))]Promise.allSettled(promises)  .then(([response1, response2, response3]) => {    console.log(response1)    // { status: 'fulfilled', value: 1 }    console.log(response2)    // { status: 'rejected', reason: 'error' }    console.log(response3)    // { status: 'fulfilled', value: 3 }})
          const promises = [
  new Promise(resolve => setTimeout(() => resolve(1), 3000)),
  new Promise((resolve, reject) => setTimeout(() => reject('error'), 2000)),
  new Promise(resolve => setTimeout(() => resolve(3), 1000))
]

Promise.allSettled(promises)
  .then(([response1, response2, response3]) => {
    console.log(response1)
    // { status: 'fulfilled', value: 1 }
    console.log(response2)
    // { status: 'rejected', reason: 'error' }
    console.log(response3)
    // { status: 'fulfilled', value: 3 }
})

        
        
          
        
      

Если промис выполнился успешно, то на выходе получаем объект с двумя свойствами — status и value. status будет содержать строку 'fulfilled', а value — значение, которое передали при вызове resolve у промиса:

        
          
          { status: 'fulfilled', value: значение }
          { status: 'fulfilled', value: значение }

        
        
          
        
      

Если промис выполнился с отказом, то на выходе получаем объект с двумя свойствами — status и reason. status будет содержать строку 'rejected', а reason — значение, которое передали при вызове reject у промиса:

        
          
          { status: 'rejected', reason: значение }
          { status: 'rejected', reason: значение }

        
        
          
        
      

На практике

Скопировано

Вадим Стебаков советует

Скопировано

🛠 Метод применяется для запросов к API. Он особенно удобен, когда запросы независимы и ошибка в одном не влияет на другие, так как Promise.allSettled() дождётся завершения всех запросов. Если же запросы зависимы, то лучше использовать метод Promise.all().

        
          
          const urls = [  'https://jsonplaceholder.typicode.com/todos/1',  'https://jsonplaceholder.typicode.org/i_need_an_error',]const arrayFetchData = urls.map(url => fetch(url).then(res => res.json()))Promise.allSettled(arrayFetchData)  .then((res) => {    // res — массив результатов выполнения промисов    res.forEach(item => {      console.log(item)    })  })
          const urls = [
  'https://jsonplaceholder.typicode.com/todos/1',
  'https://jsonplaceholder.typicode.org/i_need_an_error',
]
const arrayFetchData = urls.map(url => fetch(url).then(res => res.json()))

Promise.allSettled(arrayFetchData)
  .then((res) => {
    // res — массив результатов выполнения промисов
    res.forEach(item => {
      console.log(item)
    })
  })

        
        
          
        
      

В консоли получим:

        
          
          // { status: 'fulfilled', value: { userId: 1, id: 1, ... } }// { status: 'rejected', reason: 'TypeError: Failed to fetch...' }
          // { status: 'fulfilled', value: { userId: 1, id: 1, ... } }
// { status: 'rejected', reason: 'TypeError: Failed to fetch...' }

        
        
          
        
      

🛠 Метод Promise.allSettled() появился в спецификации языка недавно, а именно ES2020, возможно вам понадобится полифил.

На собеседовании

Скопировано
Задать вопрос в рубрику
🤚 Я знаю ответ

Алексей Фомин  отвечает

Скопировано

Теория

Скопировано

Для начала вспомним работу оригинального Promise.allSettled.

Он принимает коллекцию промисов, начинает одновременно их выполнять и возвращает массив результатов исполнения полученных промисов. Объект каждого результата содержит свойство status. Если status имеет значение fulfilled (выполнено), то объект будет содержать свойство value. Если status имеет значение rejected (отклонено), то объект будет содержать свойство reason. Свойства value или reason будут содержать значение, с которым был выполнен или отклонён промис.

Подсказки

Скопировано

Сначала попробуйте решить самостоятельно. Если не получается, вот подсказки:

  1. Можно воспользоваться методом Promse.all.
  2. Возвращаться должен массив с объектами.
  3. По завершению каждого промиса:
  • если промис был выполнен, то запипишем в поле status значение fulfilled, а в поле value — результат выполнения промиса.
  • если промис был отклонён, то запипишем в поле status значение rejected, а в поле reason — значение с текущей ошибкой, по которой он был отклонён.

Решение

Скопировано

Напишем код:

        
          
          // На вход приходит массив промисовPromise.allSettled = (promises) => {  // Запустим все промисы с помощью .all  return Promise.all(    promises.map((promise) =>      Promise.resolve(promise)        // Если промис завершается успешно        .then((value) => ({ status: "fulfilled", value }))        // Если промис был отклонён        .catch((e) => ({ status: "rejected", reason: e }))    )  )}
          // На вход приходит массив промисов
Promise.allSettled = (promises) => {
  // Запустим все промисы с помощью .all
  return Promise.all(
    promises.map((promise) =>
      Promise.resolve(promise)
        // Если промис завершается успешно
        .then((value) => ({ status: "fulfilled", value }))
        // Если промис был отклонён
        .catch((e) => ({ status: "rejected", reason: e }))
    )
  )
}

        
        
          
        
      

Тесты

Скопировано

Теперь протестируем наш полифил.

        
          
          // Создадим несколько промисов для тестированияconst a = new Promise((resolve) => setTimeout(() => { resolve(3) },200))const b = new Promise((resolve,reject) => reject('error'))const c = new Promise((resolve) => resolve(5))// ВыполнимPromise  .allSettled([a, b, c])  .then(console.log)/*[{status: 'fulfilled', value: 3},{status: 'rejected', reason: 'error'},{status: 'fulfilled', value: 5}]*/
          // Создадим несколько промисов для тестирования
const a = new Promise((resolve) => setTimeout(() => { resolve(3) },200))
const b = new Promise((resolve,reject) => reject('error'))
const c = new Promise((resolve) => resolve(5))

// Выполним
Promise
  .allSettled([a, b, c])
  .then(console.log)

/*
[
{status: 'fulfilled', value: 3},
{status: 'rejected', reason: 'error'},
{status: 'fulfilled', value: 5}
]
*/