本杰明的回答为解决这个问题提供了一个很好的抽象,但我希望有一个不那么抽象的解决方案。解决这个问题的显式方法是简单地对内部promise调用.catch,并从它们的回调返回错误。
let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });
Promise.all([c, d])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
更进一步,你可以写一个通用的catch处理程序,看起来像这样:
const catchHandler = error => ({ payload: error, resolved: false });
然后你就可以
> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]
这样做的问题是,被捕获的值将与未被捕获的值有不同的接口,所以要清理这个,你可以这样做:
const successHandler = result => ({ payload: result, resolved: true });
现在你可以这样做:
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
为了保持干燥,你看到了本杰明的答案:
const reflect = promise => promise
.then(successHandler)
.catch(catchHander)
现在是什么样子
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
第二种解决方案的优点是它是抽象的和DRY的。缺点是你有更多的代码,你必须记住反映你所有的承诺,使事情一致。
我将把我的解决方案描述为explicit和KISS,但确实不那么健壮。接口不能保证您确切地知道承诺是成功了还是失败了。
例如,你可以这样写:
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
这个不会被。catch抓到,所以
> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]
没有办法判断哪个是致命的,哪个不是。如果这很重要,那么您将希望强制和接口跟踪它是否成功(这是reflect所做的)。
如果你只是想优雅地处理错误,那么你可以把错误视为未定义的值:
> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]
在我的例子中,我不需要知道错误或它是如何失败的——我只关心是否有值。我将让生成承诺的函数负责记录特定的错误。
const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});
这样,应用程序的其余部分就可以忽略它的错误,并将其视为一个未定义的值。
我希望我的高级函数安全失败,而不担心它的依赖项失败的细节,当我必须做出权衡时,我也更喜欢KISS而不是DRY——这就是我最终选择不使用reflect的原因。