Обработка ошибок в Promise.все
у меня есть множество обещаний, которые я решаю с обещанием.все(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
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
- это все или ничего. Он решает, как только все обещания в массиве разрешаются или отклоняются, как только один из них отвергает. Другими словами, он либо решает с массивом всех разрешенных значений, либо отклоняет с одной ошибкой.
некоторые библиотеки имеют что-то под названием Promise.when
, который, как я понимаю, будет вместо этого ждать все обещает в массиве либо разрешить, либо отклонить, но я не знаком с ним, и это не в ЕС6.
код
Я согласен с другими, что исправление должно работать. Он должен разрешаться с помощью массива, который может содержать сочетание успешных значений и объектов ошибок. Необычно передавать объекты ошибок в пути успеха, но, предполагая, что ваш код ожидает их, я не вижу проблем с этим.
единственная причина я могу думать, почему это "не рассосется", что это не в коде, вы не с нами и почему вы не видите любое сообщение об ошибке связано с тем, что эта цепочка обещаний не заканчивается финальным уловом (насколько Вы нам показываете).
Я взял на себя смелость разложить на множители "существующую цепь" из вашего примера и завершить цепочку с помощью улова. Это может быть неправильно для вас, но для людей, читающих это, важно всегда либо возвращать, либо прекращать цепочки, или потенциальные ошибки, даже ошибки кодирования, будут скрыты (что, как я подозреваю, произошло здесь):
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}`);
});
Если вы используете библиотеку qhttps://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
позволяя обещает неудачу. Чтобы это произошло, просто не используйте any 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 promiseallend
2017-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'}