Обработка ошибок в обещании.все
у меня есть множество обещаний, которые я решаю с обещанием.все(arrayOfPromises);
Я продолжаю цепочку обещаний. Выглядит примерно так
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
Я хочу добавить оператор catch для обработки отдельного обещания в случае его ошибок, но когда я пытаюсь, обещаю.all возвращает первую ошибку, которую он находит (игнорирует остальные), а затем я не могу получить данные из остальных обещаний в массиве (это не ошибка).
Я пробовал делать что-то вроде ..
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler()
.then(function(data) {
return data;
})
.catch(function(err) {
return err
});
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
но это не решает.
спасибо!
--
Edit:
то, что ответы ниже сказали, было полностью правдой, код был нарушен по другим причинам. В случае, если кто-то заинтересован, это решение я в конечном итоге ...
Node Express Server Chain
serverSidePromiseChain
.then(function(AppRouter) {
var arrayOfPromises = state.routes.map(function(route) {
return route.async();
});
Promise.all(arrayOfPromises)
.catch(function(err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
// full array of resolved promises;
})
};
вызов API (маршрут.асинхронный вызов)
return async()
.then(function(result) {
// dispatch a success
return result;
})
.catch(function(err) {
// dispatch a failure and throw error
throw err;
});
поставить .поймать за обещание.все раньше этот.затем, похоже, служили цели ловли любых ошибок из исходных обещаний, но затем возвращали весь массив к следующему .тогда
спасибо!
8 ответов:
Promise.all- это все или ничего. Он разрешает один раз все обещания в массиве resolve, или отклонить, как только один из них отвергает. Другими словами, он либо разрешает массив всех разрешенных значений, либо отклоняет с одной ошибкой.В некоторых библиотеках есть что-то под названием
Promise.when, который, как я понимаю, вместо этого будет ждать все обещает в массиве либо разрешить, либо отклонить, но я не знаком с ним, и его нет ES6.код
Я согласен с другими, что исправление должно работать. Он должен разрешаться с помощью массива, который может содержать сочетание успешных значений и объектов ошибок. Необычно передавать объекты ошибок в пути успеха, но предполагая, что ваш код ожидает их, я не вижу проблем с этим.
единственная причина, по которой я могу думать о том, почему он "не разрешит", заключается в том, что он не работает в коде, который Вы нам не показываете, и причина, по которой вы не видите любое сообщение об ошибке связано с тем, что эта цепочка обещаний не заканчивается окончательным уловом (насколько Вы нам показываете в любом случае).
Я взял на себя смелость разложить "существующую цепь" из вашего примера и завершить цепь с уловом. Это может быть неправильно для вас, но для людей, читающих это, важно всегда либо возвращать, либо прекращать цепочки, либо потенциальные ошибки, даже ошибки кодирования, будут скрыты (что, как я подозреваю, произошло здесь):
Promise.all(state.routes.map(function(route) { return route.handler.promiseHandler().catch(function(err) { return err; }); })) .then(function(arrayOfValuesOrErrors) { // handling of my array containing values and/or errors. }) .catch(function(err) { console.log(err.message); // some coding error in handling happened });
продолжить
Promise.allцикл (даже когда обещание отклоняется) я написал функцию полезности, которая называетсяexecuteAllPromises. Эта служебная функция возвращает объект сresultsиerrors.идея в том, что все обещания вы передаете
executeAllPromisesбудет обернут в новое обещание, которое всегда будет решать. Новое обещание решает с массивом, который имеет 2 пятна. Первое место содержит разрешающее значение (если оно есть) , а второе место сохраняет ошибку (если завернутое обещание отходы.)в качестве заключительного шага
executeAllPromisesнакапливает все значения завернутых обещаний и возвращает конечный объект с массивом дляresultsи массив заerrors.вот код:
function executeAllPromises(promises) { // Wrap all Promises in a Promise that will always "resolve" var resolvingPromises = promises.map(function(promise) { return new Promise(function(resolve) { var payload = new Array(2); promise.then(function(result) { payload[0] = result; }) .catch(function(error) { payload[1] = error; }) .then(function() { /* * The wrapped Promise returns an array: * The first position in the array holds the result (if any) * The second position in the array holds the error (if any) */ resolve(payload); }); }); }); var errors = []; var results = []; // Execute all wrapped Promises return Promise.all(resolvingPromises) .then(function(items) { items.forEach(function(payload) { if (payload[1]) { errors.push(payload[1]); } else { results.push(payload[0]); } }); return { errors: errors, results: results }; }); } var myPromises = [ Promise.resolve(1), Promise.resolve(2), Promise.reject(new Error('3')), Promise.resolve(4), Promise.reject(new Error('5')) ]; executeAllPromises(myPromises).then(function(items) { // Result var errors = items.errors.map(function(error) { return error.message }).join(','); var results = items.results.join(','); console.log(`Executed all ${myPromises.length} Promises:`); console.log(`— ${items.results.length} Promises were successful: ${results}`); console.log(`— ${items.errors.length} Promises failed: ${errors}`); });
Если вы используете библиотеку q https://github.com/kriskowal/q он имеет Q. allSettled() метод, который может решить эту проблему вы можете обрабатывать каждое обещание в зависимости от его состояния либо заполнено, либо отклонено так что
existingPromiseChain = existingPromiseChain.then(function() { var arrayOfPromises = state.routes.map(function(route){ return route.handler.promiseHandler(); }); return q.allSettled(arrayOfPromises) }); existingPromiseChain = existingPromiseChain.then(function(arrayResolved) { //so here you have all your promises the fulfilled and the rejected ones // you can check the state of each promise arrayResolved.forEach(function(item){ if(item.state === 'fulfilled'){ // 'rejected' for rejected promises //do somthing } else { // do something else } }) // do stuff with my array of resolved promises, eventually ending with a res.send(); });
мы должны написать пользовательский обещание.все.)( Вот решение, которое я использую в своем проекте. Ошибка будет возвращена как обычный результат. После того, как все обещания закончатся, мы можем отфильтровать ошибку.
const Promise_all = promises => { return new Promise((resolve, reject) => { const results = []; let count = 0; promises.forEach((promise, idx) => { promise .catch(err => { return err; }) .then(valueOrError => { results[idx] = valueOrError; count += 1; if (count === promises.length) resolve(results); }); }); }); }; const results = await Promise_all(promises) const validResults = results.filter(result => !(result instanceof Error));
для тех, кто использует ES8, которые спотыкаются здесь, вы можете сделать что-то вроде следующего, используя асинхронные функции:
var arrayOfPromises = state.routes.map(async function(route){ try { return await route.handler.promiseHandler(); } catch(e) { // Do something to handle the error. // Errored promises will return whatever you return here (undefined if you don't return anything). } }); var resolvedPromises = await Promise.all(arrayOfPromises);
вот так
Promise.allпредназначен для работы. Если одно обещаниеreject()' s, весь метод сразу терпит неудачу.есть случаи использования, когда можно было бы иметь
Promise.allучитывая обещания потерпеть неудачу. Чтобы это произошло, просто не используйте никакихreject()заявления в свое обещание. Однако, чтобы убедиться, что ваше приложение / скрипт не замерзает в случае какого-либо одного базового обещания никогда получает ответ, вам нужно поставить тайм-аут на него.function getThing(uid,branch){ return new Promise(function (resolve, reject) { xhr.get().then(function(res) { if (res) { resolve(res); } else { resolve(null); } setTimeout(function(){reject('timeout')},10000) }).catch(function(error) { resolve(null); }); }); }
мы можем обрабатывать отказ на уровне отдельных обещаний, поэтому, когда мы получим результаты в нашем массиве результатов, индекс массива, который был отклонен, будет неопределенным, и мы можем обрабатывать эту ситуацию по мере необходимости. И использовать оставшиеся результаты.
здесь я отклонил первое обещание, поэтому оно не определено, но мы можем использовать результат второго обещания, который находится в индексе 1.
var manyPromises = Promise.all([ func1(), func2()]).then((result) => { console.log(result[0]); // undefined console.log(result[1]); // func2 }); function func1() { return new Promise( (res, rej) => { rej("func1");}).catch(err => { console.log(`error handled ${err}`); }); } function func2() { return new Promise( (res, rej) => { res("func2");}).catch(err => { console.log(`error handled ${err}`); }); }
Я написал библиотеку npm, чтобы справиться с этой проблемой более красиво. https://github.com/wenshin/promiseallend
установить
npm i --save promiseallend2017-02-25 новый api, это не нарушает принципы обещания
const promiseAllEnd = require('promiseallend'); const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)]; const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)}; // input promises with array promiseAllEnd(promises, { unhandledRejection(error, index) { // error is the original error which is 'error'. // index is the index of array, it's a number. console.log(error, index); } }) // will call, data is `[1, undefined, 2]` .then(data => console.log(data)) // won't call .catch(error => console.log(error.detail)) // input promises with object promiseAllEnd(promisesObj, { unhandledRejection(error, prop) { // error is the original error. // key is the property of object. console.log(error, prop); } }) // will call, data is `{k1: 1, k3: 2}` .then(data => console.log(data)) // won't call .catch(error => console.log(error.detail)) // the same to `Promise.all` promiseAllEnd(promises, {requireConfig: true}) // will call, `error.detail` is 'error', `error.key` is number 1. .catch(error => console.log(error.detail)) // requireConfig is Array promiseAllEnd(promises, {requireConfig: [false, true, false]}) // won't call .then(data => console.log(data)) // will call, `error.detail` is 'error', `error.key` is number 1. .catch(error => console.log(error.detail)) // requireConfig is Array promiseAllEnd(promises, {requireConfig: [true, false, false]}) // will call, data is `[1, undefined, 2]`. .then(data => console.log(data)) // won't call .catch(error => console.log(error.detail))--------------------------------
старый плохой api, не используйте его!
let promiseAllEnd = require('promiseallend'); // input promises with array promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)]) .then(data => console.log(data)) // [1, undefined, 2] .catch(error => console.log(error.errorsByKey)) // {1: 'error'} // input promises with object promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)}) .then(data => console.log(data)) // {k1: 1, k3: 2} .catch(error => console.log(error.errorsByKey)) // {k2: 'error'}