Допустим, у меня есть набор Promise
s, которые делают сетевые запросы, один из которых завершится ошибкой:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Допустим, я хочу подождать, пока все это не закончится, независимо от того, потерпел ли он неудачу. Может быть сетевая ошибка для ресурса, без которого я могу жить, но который, если я могу получить, я хочу, прежде чем продолжить. Я хочу изящно обрабатывать сбои сети.
Поскольку Promises.all
для этого не остается места, каков рекомендуемый шаблон для обработки этого без использования библиотеки обещаний?
javascript
promise
es6-promise
Натан Хаген
источник
источник
allSettled
удовлетворить ваши потребности, без необходимости кататься самостоятельно.Promise.all
будет отклонен, как только одно обещание будет отклонено , поэтому предложенная вами идиома не гарантирует, что все обещания будут выполнены.Ответы:
Обновление, вы, вероятно, хотите использовать встроенный нативный
Promise.allSettled
:Как забавный факт, этот ответ ниже был предшествующим уровнем техники при добавлении этого метода к языку:]
Конечно, вам просто нужно
reflect
:Или с ES5:
Или в вашем примере:
источник
reflect
распространенным словом в информатике? Можете ли вы дать ссылку, где это объясняется, как в Википедии или что-то. Я искал трудно,Promise.all not even first reject
но не знал, чтобы искать "Отражение". Должен ли ES6 иметьPromise.reflect
что-то вроде «Promise.all, но на самом деле все»?Подобный ответ, но более идиоматичный для ES6 возможно:
В зависимости от типа (ов) из возвращаемых значений, ошибки часто можно выделить достаточно легко (например , использование
undefined
для «не важно»,typeof
для обычных значений необъектных,result.message
, иresult.toString().startsWith("Error:")
т.д.)источник
.map(p => p.catch(e => e))
деталь превращает все отклонения в разрешенные значения, поэтомуPromise.all
все еще ожидает завершения всего, независимо от того, сколько времени они принимают, независимо от того, разрешают или отклоняют отдельные функции. Попробуй это..catch(e => console.log(e));
никогда не вызывается, потому что это никогда не перестаетcatch
как правило, является хорошей практикой ИМХО .e
и возвращает ее как обычное (успешное) значение. Так же, какp.catch(function(e) { return e; })
только короче.return
неявно.Ответ Бенджамина предлагает отличную абстракцию для решения этой проблемы, но я надеялся на менее абстрактное решение. Явный способ решить эту проблему - просто вызвать
.catch
внутренние обещания и вернуть ошибку из их обратного вызова.Сделав еще один шаг вперед, вы можете написать общий обработчик catch, который будет выглядеть так:
тогда вы можете сделать
Проблема в том, что уловленные значения будут иметь другой интерфейс, чем не уловленные значения, поэтому для очистки вы можете сделать что-то вроде:
Так что теперь вы можете сделать это:
Затем, чтобы сохранить его сухим, вы получите ответ Бенджамина:
где это сейчас выглядит
Преимущества второго решения в том, что оно абстрактное и СУХОЕ. Недостатком является то, что у вас есть больше кода, и вы должны помнить, чтобы отражать все ваши обещания, чтобы сделать вещи согласованными.
Я бы охарактеризовал свое решение как явное и ПОЦЕЛУЮ, но на самом деле менее надежное. Интерфейс не гарантирует, что вы точно знаете, было ли обещание выполнено или не выполнено.
Например, у вас может быть это:
Это не попадется
a.catch
, поэтомуНевозможно сказать, какой из них был смертельным, а какой нет. Если это важно, то вы захотите применить и интерфейс, который отслеживает, был ли он успешным или нет (что
reflect
делает).Если вы просто хотите корректно обрабатывать ошибки, то вы можете просто рассматривать ошибки как неопределенные значения:
В моем случае мне не нужно знать об ошибке или о том, как она произошла - мне просто важно, есть ли у меня значение или нет. Я позволю функции, которая генерирует обещание, беспокоиться о регистрации конкретной ошибки.
Таким образом, остальная часть приложения может игнорировать свою ошибку, если она хочет, и обрабатывать ее как неопределенное значение, если оно хочет.
Я хочу, чтобы мои высокоуровневые функции благополучно выходили из строя, и не беспокоился о деталях, почему их зависимости перестали работать, и я также предпочитаю, чтобы KISS СУХОЙ, когда я должен сделать этот компромисс - именно поэтому я решил не использовать
reflect
.источник
Promise
s. Хотя вашreflect
код улучшает повторное использование, он также устанавливает другой уровень абстракции. Поскольку ответ Натана до сих пор получил лишь небольшую часть голосов против вас, мне интересно, является ли это признаком проблемы с его решением, которую я еще не узнал.new Promise((res, rej) => res(new Error('Legitimate error'))
не будет отличаться отnew Promise(((res, rej) => rej(new Error('Illegitimate error'))
? Или, кроме того, вы не сможете отфильтроватьx.status
? Я добавлю этот момент в свой ответ, чтобы разница стала более яснойPromise.all()
варианте, а затем потребителю Promise становится известно, что конкретное обещание не будет отклонено, а будет проглотить это ошибки. Фактически,reflect()
метод можно сделать менее «абстрактным» и более явным, назвав егоPromiseEvery(promises).then(...)
. Сложность ответа выше по сравнению с ответами Бенджамина должна многое сказать об этом решении.Есть законченное предложение для функции, которая может выполнить это изначально, в vanilla Javascript:
Promise.allSettled
она перешла на этап 4, официально зарегистрирована в ES2020 и реализована во всех современных средах . Это очень похоже наreflect
функцию в этом другом ответе . Вот пример со страницы предложения. Раньше вы должны были сделать:Используя
Promise.allSettled
вместо этого, выше будет эквивалентно:Те, кто использует современную среду, смогут использовать этот метод без каких-либо библиотек . В них следующий фрагмент должен работать без проблем:
Вывод:
Для более старых браузеров, есть соответствующая спецификация polyfill здесь .
источник
Мне очень нравится ответ Бенджамина, и то, как он в основном превращает все обещания в всегда решающие, но иногда с ошибками, как результат. :)
Вот моя попытка по вашему запросу на тот случай, если вы искали альтернативы. Этот метод просто обрабатывает ошибки как допустимые результаты и кодируется аналогично
Promise.all
другому:источник
settle
. У нас это тоже есть в bluebird, мне нравится лучше размышлять, но это жизнеспособное решение для случая, когда у вас есть это для массива.Promise
конструктор правильно (и избегать этойvar resolve
штуки)?Он
Promise.all
проглотит любое отклоненное обещание и сохранит ошибку в переменной, поэтому он вернется, когда все обещания будут разрешены. Затем вы можете повторно выбросить ошибку или сделать что угодно. Таким образом, я полагаю, вы получите последний отказ вместо первого.источник
err.push(error)
ошибки, сделав его массивом и использовав его , так что все ошибки могут быть разбиты.У меня была такая же проблема, и я решил ее следующим образом:
В этом случае
Promise.all
будет ждать, пока каждое Обещание придетresolved
илиrejected
заявит.И имея это решение, мы «останавливаем
catch
выполнение» неблокирующим способом. Фактически, мы ничего не останавливаем, мы просто возвращаем обратноPromise
в состояние ожидания, которое возвращает другое,Promise
когда оно разрешено после истечения времени ожидания.источник
Promise.all
. Я ищу способ слушать, когда все обещания были вызваны, но не вызывать их сам. Спасибо.all()
делает это, он ожидает выполнения всех обещаний или отклонения хотя бы одного из них..all
что все сгорело .then
или.all
call), но они запускаются при создании.Это должно соответствовать тому, как это делает Q :
источник
Ответ Бенджамина Грюнбаума, конечно, велик. Но я также вижу, что точка зрения Натана Хагена с уровнем абстракции кажется туманной. Короткие свойства объекта, такие как
e & v
, также не помогают, но, конечно, это можно изменить.В Javascript есть стандартный объект Error, называемый
Error
,. В идеале вы всегда бросаете экземпляр / потомок этого. Преимущество в том, что вы можете сделатьinstanceof Error
, и вы знаете, что-то является ошибкой.Таким образом, используя эту идею, вот мой взгляд на проблему.
В основном, перехватите ошибку, если ошибка не относится к типу Error, оберните ошибку внутри объекта Error. Полученный массив будет иметь либо разрешенные значения, либо объекты Error, которые вы можете проверить.
Экземпляр внутри улова, в случае, если вы используете внешнюю библиотеку, которая, возможно, сделала
reject("error")
, вместоreject(new Error("error"))
.Конечно, у вас могут быть обещания, если вы разрешите ошибку, но в этом случае, скорее всего, все равно будет иметь смысл рассматривать ошибку, как показано в последнем примере.
Еще одно преимущество этого - уничтожение массива - это просто.
Вместо
Вы можете утверждать, что
!error1
проверка проще, чем instanceof, но вам также придется уничтожить и то, и другоеv & e
.источник
Вместо отклонения разрешите его с помощью объекта. Вы могли бы сделать что-то подобное, когда вы выполняете обещание
источник
Я думаю , что следующие предложения немного другой подход ... сравнить
fn_fast_fail()
сfn_slow_fail()
... хотя последний не провалится как таковой ... вы можете проверить , если один или обаa
иb
является экземпляромError
иthrow
что ,Error
если вы хотите , чтобы добратьсяcatch
блок (напримерif (b instanceof Error) { throw b; }
). См jsfiddle .источник
Вот мой обычай
settledPromiseAll()
По сравнению с
Promise.all
Если все обещания разрешены, он работает точно так же, как стандартный.
Если одно из нескольких обещаний отклонено, оно возвращает первое отклоненное, почти то же самое, что и стандартное, но в отличие от этого ждет все обещания, чтобы разрешить / отклонить.
Для смелых мы могли бы изменить
Promise.all()
:ОСТОРОЖНО . В общем случае мы никогда не меняем встроенные модули, так как это может привести к поломке других не связанных библиотек JS или столкнуться с будущими изменениями стандартов JS.
My
settledPromiseall
обратно совместим сPromise.all
и расширяет его функциональность.Люди, которые разрабатывают стандарты - почему бы не включить это в новый стандарт Promise?
источник
Promise.all
с использованием современногоasync/await
подходаисточник
Я бы сделал:
источник
Вы можете выполнять свою логику последовательно через синхронного исполнителя nsynjs . Он будет приостанавливать выполнение каждого обещания, ждать разрешения / отклонения и либо присваивать результат разрешения
data
свойству, либо выдавать исключение (для обработки этого вам потребуется блок try / catch). Вот пример:источник
Я использую следующие коды с ES5.
Подпись использования так же, как
Promise.all
. Основное отличие состоит в том, чтоPromise.wait
будут ждать все обещания, чтобы закончить свою работу.источник
Я знаю, что на этот вопрос есть много ответов, и я уверен, что должны (если не все) правильно. Однако мне было очень трудно понять логику / последовательность этих ответов.
Поэтому я посмотрел на Исходную реализацию
Promise.all()
и попытался имитировать эту логику - за исключением того, что не останавливал выполнение в случае сбоя одного Обещания.Объяснение:
- Зациклите ввод
promisesList
и выполните каждое обещание.- Независимо от того, было ли обещание разрешено или отклонено: сохраните результат обещания в
result
массиве в соответствии сindex
. Сохраните также статус разрешения / отклонения (isSuccess
).- После выполнения всех обещаний верните одно обещание с результатом всех остальных.
Пример использования:
источник
Promise.all
себя, есть слишком много вещей, которые пойдут не так, как надо. Например, ваша версия не обрабатывает пустые данные.Я не знаю, какую библиотеку обещаний вы используете, но у большинства есть что-то вроде allSettled .
Изменить: Хорошо, так как вы хотите использовать обычный ES6 без внешних библиотек, такого метода не существует.
Другими словами: вы должны вручную просмотреть свои обещания и разрешить новое объединенное обещание, как только все обещания будут выполнены.
источник