- PVSM.RU - https://www.pvsm.ru -
На 71-м митинге Ecma TC39 будет рассматриваться проект и эталонная реализация Promise.allSettled
— третьего из четырех основных комбинаторов промисов.
Авторы: Джейсон Вильямс (BBC), Роберт Памли (Bloomberg), Матиас Байненс (Google)
Чемпион: Матиас Байненс (Google)
Этап: 3
Для любителей подкастов, продублировано на YouTube [1].
В мире промисов существует четыре основных комбинатора:
Promise.all
(ES2015; замыкается на первом отклоненном промисе)Promise.race
(ES2015; замыкается на первом разрешенном промисе)Promise.any
(Stage 1; замыкается на первом удовлетворенном промисе)Promise.allSettled
(Stage 3 → Stage 4; не замыкается)Все они широко представлены в обычных пользовательских библиотеках, каждый из них полезен сам по себе и подходит в различных ситуациях.
Основное применение этого комбинатора наступает, когда хочется выполнить действие сразу после завершения множества запросов, вне зависимости, закончились ли они успехом или неудачей. Остальные комбинаторы промисов замыкаются (short-circuit), выбрасывая результаты входящих значений, проигравших в гонке за определённым состоянием системы. Promise.allSettled
уникален тем, что всегда ожидает всех, за кого отвечает.
Promise.allSettled
возвращает промис, который выполняется с возвращением массива снапшотов состояний промисов, но лишь только после того, как совершенно все исходные промисы разрешены (settled).
Мы говорим, что промис разрешен (settled), если он не подвис в ожидании (pending), т.е. когда он либо удовлетворён, либо отклонён — одно из двух. Чтобы разобраться в терминологии, взгляните на старый документ States and Fates [2].
А ещё, это имя, allSettled
, широко используется в существующих библиотеках, реализующих данную функциональность. Список будет ниже.
Представьте, вам нужно проитерироваться по массиву промисов и вернуть новое значение с известным статусом (которое возникает в любом из двух возможных ответвлений логики).
function reflect(promise) {
return promise.then(
(v) => {
return { status: 'fulfilled', value: v };
},
(error) => {
return { status: 'rejected', reason: error };
}
);
}
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.all(promises.map(reflect));
const successfulPromises = results.filter(p => p.status === 'fulfilled');
Предлагаемое API позволяет разработчику обработать эти варианты, без необходимости создавать функцию reflect
самостоятельно, или заниматься хранением результатов во временных переменных. Новое API выглядит так:
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');
Если же нам почему-то нужны отклонённые промисы, то вероятно, нужно собрать причины произошедшего. allSettled
позволяет сделать это так же просто.
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const errors = results
.filter(p => p.status === 'rejected')
.map(p => p.reason);
Довольно распространённым является желание знать, что все запросы выполнились, вне зависимости от состояния каждого из них. Это важно, когда хочется в будущем заняться постепенным улучшением. Не всегда нам нужно получить от API ответ.
const urls = [ /* ... */ ];
const requests = urls.map(x => fetch(x)); // Представьте, что-то из этого увенчается успехом, а что-то - нет.
// Вот этот комбинатор остановится на первом же отказе, а ответы потеряются.
try {
await Promise.all(requests);
console.log('Все запросы вернулись, можно убрать полоску загрузки.');
} catch {
console.log('Какой-то из запросов явно отвалился, но другие могут продолжать работать. Ой.');
}
С использованием Promise.allSettled
можно написать нечто, что больше соответствует нашим ожиданиям.
// Мы точно знаем, что все запросы к API уже отработали.
Promise.allSettled(requests).finally(() => {
console.log('Все запросы завершены: успешно или с ошибкой, сейчас всё равно');
removeLoadingIndicator();
});
Похожая функциональность существует и в других языках, под другими именами. Поскольку не существует универсального механизма, совместимого сразу со множеством языков, этот документ следует прмеру наименований из библиотек, перечисленных выше. Вот как это выглядит в других языках:
futures::join
;Task.WhenAll
. Можно использовать либо try/catch, либо TaskContinuationOptions.OnlyOnFaulted
;asyncio.wait
с опцией ALL_COMPLETED
CompletableFuture.allOf
Автор: Олег Чирухин
Источник [24]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/323881
Ссылки в тексте:
[1] на YouTube: https://www.youtube.com/watch?v=jy5JhorT79k
[2] States and Fates: https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md
[3] https://www.npmjs.com/package/promise.allsettled: https://www.npmjs.com/package/promise.allsettled
[4] https://www.npmjs.com/package/q: https://www.npmjs.com/package/q
[5] https://www.npmjs.com/package/rsvp: https://www.npmjs.com/package/rsvp
[6] http://bluebirdjs.com/docs/api/reflect.html: http://bluebirdjs.com/docs/api/reflect.html
[7] https://www.npmjs.com/package/promise-settle: https://www.npmjs.com/package/promise-settle
[8] https://github.com/cujojs/when/blob/master/docs/api.md#whensettle: https://github.com/cujojs/when/blob/master/docs/api.md#whensettle
[9] https://www.npmjs.com/package/es2015-promise.allsettled: https://www.npmjs.com/package/es2015-promise.allsettled
[10] https://www.npmjs.com/package/promise-all-settled: https://www.npmjs.com/package/promise-all-settled
[11] https://www.npmjs.com/package/maybe: https://www.npmjs.com/package/maybe
[12] https://www.bennadel.com/blog/3289-implementing-q-s-allsettled-promise-method-in-bluebird.htm: https://www.bennadel.com/blog/3289-implementing-q-s-allsettled-promise-method-in-bluebird.htm
[13] http://exploringjs.com/es6/ch_promises.html: http://exploringjs.com/es6/ch_promises.html
[14] https://github.com/kriskowal/q/issues/257: https://github.com/kriskowal/q/issues/257
[15] September 2018: https://tc39.github.io/tc39-notes/2018-09_sept-27.html#promiseallsettled-for-stage-1
[16] January 2019: https://tc39.github.io/tc39-notes/2019-01_jan-30.html#promiseallsettled
[17] March 2019: https://github.com/rwaldron/tc39-notes/blob/master/meetings/2019-03/mar-26.md#promiseallsettled-for-stage-3
[18] Исходник на Ecmarkup: https://github.com/tc39/proposal-promise-allSettled/blob/master/spec.html
[19] В виде HTML: https://tc39.github.io/proposal-promise-allSettled/
[20] V8: https://bugs.chromium.org/p/v8/issues/detail?id=9060
[21] SpiderMonkey: https://bugzilla.mozilla.org/show_bug.cgi?id=1539694
[22] JavaScriptCore: https://bugs.webkit.org/show_bug.cgi?id=197600
[23] Chakra: https://github.com/microsoft/ChakraCore/pull/6138
[24] Источник: https://habr.com/ru/post/459970/?utm_source=habrahabr&utm_medium=rss&utm_campaign=459970
Нажмите здесь для печати.