Handling errors in Promise.all
我有一个用Promise.all(arrayOfPromises)解决的Promises数组;
我继续继续诺言链。看起来像这样
1 2 3 4 5 6 7 8 9 10 | 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语句来处理单个promise,以防万一出错,但是当我尝试时,Promise.all返回它找到的第一个错误(忽略其余错误),然后我无法从其余错误中获取数据数组中的promise(没有错误)。
我尝试做类似..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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(); }); |
但这并不能解决。
谢谢!
-
编辑:
下面的答案完全正确,但代码由于其他原因而中断。如果有人感兴趣,这就是我最终得到的解决方案...
节点快速服务器链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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调用(route.async调用)
1 2 3 4 5 6 7 8 9 | return async() .then(function(result) { // dispatch a success return result; }) .catch(function(err) { // dispatch a failure and throw error throw err; }); |
将.catch for Promise.all放在.then之前的目的似乎是捕获原始诺言中的任何错误,然后将整个数组返回到下一个.then。
谢谢!
有些库中有一个名为
您的密码
我在这里同意其他人的观点,认为您的修复应该可以进行。它应使用可能包含成功值和错误对象混合的数组来解析。在成功路径中传递错误对象是不寻常的,但是假设您的代码期望它们,我认为它没有问题。
我能想到它"无法解决"的唯一原因是,它在代码中失败,您没有向我们显示;而您没有看到有关此消息的任何错误消息,是因为该诺言链没有以final结尾捕获(就您向我们展示的内容而言)。
我已自由地从您的示例中排除了"现有链",并以一条链来终止该链。这可能不适合您,但是对于阅读此书的人来说,始终返回或终止链很重要,否则潜在的错误(甚至编码错误)将被隐藏(这是我怀疑在这里发生的事情):
1 2 3 4 5 6 7 8 9 10 11 | 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 }); |
新答案
1 2 | const results = await Promise.all(promises.map(p => p.catch(e => e))); const validResults = results.filter(result => !(result instanceof Error)); |
未来承诺API
- Chrome 76:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
- 您可以下载https://www.npmjs.com/package/promise.allsettled立即获取。在某些浏览器中,allSettled预先安装了浏览器本身。值得下载此软件包以省心,因为例如。 TypeScript没有allSettled的默认定义。
为了继续
这个想法是,您传递给
作为最后一步,
这是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 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}`); }); |
正如@jib所说,
Promise.all is all or nothing.
不过,您可以控制"允许"失败的某些承诺,我们希望继续进行
例如。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Promise.all([ doMustAsyncTask1, doMustAsyncTask2, doOptionalAsyncTask .catch(err => { if( /* err non-critical */) { return } // if critical then fail throw err }) ]) .then(([ mustRes1, mustRes2, optionalRes ]) => { // proceed to work with results }) |
使用异步等待-
这里,一个异步函数func1返回一个解析值,而func2在这种情况下抛出一个错误并返回null,我们可以按需要处理它并相应地返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | const callingFunction = async () => { const manyPromises = await Promise.all([func1(), func2()]); console.log(manyPromises); } const func1 = async () => { return 'func1' } const func2 = async () => { try { let x; if (!x) throw"x value not present" } catch(err) { return null } } callingFunction(); |
输出为-['func1',null]
如果您可以使用q库https://github.com/kriskowal/q
它具有q.allSettled()方法可以解决此问题
您可以根据承诺的状态来处理每个承诺,包括已履行或已拒绝
所以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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(); }); |
对于使用ES8的用户,可以使用异步功能执行以下操作:
1 2 3 4 5 6 7 8 9 10 | 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级别处理拒绝,因此当我们在结果数组中获取结果时,已被拒绝的数组索引将为
在这里,我拒绝了第一个承诺,因此它没有定义,但是我们可以使用第二个承诺的结果,即索引1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const 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) => setTimeout(() => res('func2'), 500) ); } |
您是否考虑过
它似乎旨在完全按照您的要求进行操作-一旦所有的诺言都已解决(解决/拒绝),就执行一个函数,而不管某些诺言是否被拒绝。
从MDN文档中:
如果您希望在兑现承诺后进行一些处理或清理,而不论其结果如何,则
在创建函数内联时,您可以传递一次,而不必强迫其声明两次或为其创建变量。
由于没有可靠的方法来确定诺言是否已兑现,因此finally回调将不会收到任何参数。正是这种用例适用于您不关心拒绝原因或实现价值的情况,因此不需要提供它。
与
同样,与
==后备==
如果您的JavaScript版本不支持
您始终可以将诺言返回函数包装为能够捕获故障并返回约定值(例如error.message)的方式,因此异常不会一直累积到Promise.all函数并禁用它。
1 2 3 4 5 6 7 8 9 10 11 12 13 | async function resetCache(ip) { try { const response = await axios.get(`http://${ip}/resetcache`); return response; }catch (e) { return {status: 'failure', reason: 'e.message'}; } } |
或者,如果您遇到的情况是当一个失败时您并不特别在意已解决的承诺的值,但是您仍然希望它们能够继续运行,那么您可以执行类似的操作,以在正常情况下以承诺解决问题他们全部成功,当其中任何一个失败时,就会以失败的承诺拒绝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function promiseNoReallyAll (promises) { return new Promise( async (resolve, reject) => { const failedPromises = [] const successfulPromises = await Promise.all( promises.map( promise => promise.catch(error => { failedPromises.push(error) }) ) ) if (failedPromises.length) { reject(failedPromises) } else { resolve(successfulPromises) } } ) } |
这就是
在某些情况下,可能希望
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); }); }); } |
我写了一个npm库来更漂亮地处理这个问题。
https://github.com/wenshin/promiseallend
安装
1 | npm i --save promiseallend |
2017-02-25新的API,这不是违约原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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,请勿使用!
1 2 3 4 5 6 7 8 9 10 11 | 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'} |