От автора: говоря простыми словами, промисы — это плейсхолдер для значения, которое будет доступно когда-нибудь позже. Промисы полезны при обработке асинхронных операций.
JavaScript предоставляет вспомогательную функцию Promise.all(promisesArrayOrIterable) для одновременной, параллельной обработки нескольких промисов и получения результатов в одном агрегированном массиве. Посмотрим, как это работает.
Promise.all()
Promise.all() — это встроенный хэлпер, который принимает массив промисов (или, как правило, итерацию).
1 |
const allPromise = Promise.all([promise1, promise2, ...]); |
Затем вы можете извлечь значения промисов, используя синтаксис then-able:
1 2 3 4 5 |
allPromise.then(values => { values; // [valueOfPromise1, valueOfPromise2, ...] }).catch(error => { error; // rejectReason of any first rejected promise }); |
или синтаксис async/await:
1 2 3 4 5 6 |
try { const values = await allPromise; values; // [valueOfPromise1, valueOfPromise2, ...] } catch (error) { error; // rejectReason of any first rejected promise } |
Интересная часть заключается в том, как промис, возвращаемый функцией Promise.all(), разрешается или отклоняется. Если все промисы разрешены успешно, allPromise выполняется с массивом, содержащим выполненные значения отдельных промисов. Порядок промисов в массиве имеет значение — вы получите значения именно в этом порядке.
Но если хотя бы один промис отклоняется, то allPromise отклоняется сразу (не дожидаясь разрешения других промисов).
Давайте посмотрим на нескольких примерах, как использовать Promise.all() для одновременного выполнения нескольких асинхронных операций.
Пример: все промисы выполнены.
Чтобы изучить, как работает Promise.all(), я собираюсь использовать 2 функции — resolveTimeout(value, delay) и rejectTimeout(reason, delay).
1 2 3 4 5 6 7 8 9 10 11 |
function resolveTimeout(value, delay) { return new Promise( resolve => setTimeout(() => resolve(value), delay) ); } function rejectTimeout(reason, delay) { return new Promise( (r, reject) => setTimeout(() => reject(reason), delay) ); } |
resolveTimeout(value, delay) возвращает промис, который выполняется по прошествии времени delay.
С другой стороны, rejectTimeout(reason, delay) возвращает промис, который отклоняется по причине reason (обычно с ошибкой) по прошествии времени delay.
Например, давайте получим доступ к спискам овощей и фруктов, доступных в местном продуктовом магазине. Доступ к каждому списку — асинхронная операция:
1 2 3 4 5 6 7 |
const allPromise = Promise.all([ resolveTimeout(['potatoes', 'tomatoes'], 1000), resolveTimeout(['oranges', 'apples'], 1000)]); // wait... const lists = await allPromise; // after 1 second console.log(lists); // [['potatoes', 'tomatoes'], ['oranges', 'apples']] |
const allPromise = Promise.all([…]) возвращает новый allPromise.
Затем оператор const lists = await allPromise ожидает 1 секунду, пока не будет выполнен allPromise, содержащий значения выполнения первого и второго промисов.
Наконец, lists содержит агрегированный результат: [[‘potatoes’, ‘tomatoes’], [‘oranges’, ‘apples’]].
Порядок массива промисов напрямую влияет на порядок результатов. Промис из овощей это первый элемент, а промис из фруктов — второй элемент в массиве: Promise.all([vegetablesPromise, fruitsPromise]). Массив результатов содержит значения в том же порядке — первый список овощей и второй список фруктов.
Пример: один промис отклоняется.
А теперь представьте ситуацию, когда в магазине закончились фрукты. В таком случае давайте откажемся от промиса фруктов с ошибкой new Error(‘Out of fruits!’):
1 2 3 4 5 6 7 8 9 10 |
const allPromise = Promise.all([ resolveTimeout(['potatoes', 'tomatoes'], 1000), rejectTimeout(new Error('Out of fruits!'), 1000)]); try { // wait... const lists = await allPromise; } catch (error) { // after 1 second console.log(error.message); // 'Out of fruits!'} |
В сценарии allPromise = Promise.all([…]), как обычно, возвращается промис.
Однако по прошествии 1 секунды второй промис(фрукты) отклоняется с ошибкой new Error(‘Out of fruits!’). Это приведет к отказу new Error(‘Out of fruits!’).
Даже если промисы по овощам были выполнены, Promise.all() это не принимает во внимание.
Такое поведение Promise.all([…]) называется отказоустойчивым. Если хотя бы один промис в массиве отклоняется, то результат, возвращенный allPromise = Promise.all([…]) также отклоняется — по той же причине.
Вывод
Promise.all([…]) — полезная вспомогательная функция, которая позволяет выполнять асинхронные операции параллельно, используя стратегию безотказной работы, и объединять результаты в массив.
Автор: Dmitri Pavlutin
Источник: dmitripavlutin.com
Редакция: Команда webformyself.
Читайте нас в Telegram, VK, Яндекс.Дзен