Benjamin的回答提供了一个很好的抽象方法来解决这个问题,但我希望有一个不太抽象的解决方案。明确的解决方法是在内部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))
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result))
.catch(err => console.log('Catch', err));
进一步地,你可以编写一个通用异常捕获处理程序,它看起来像这样:
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!' ]
为了遵循DRY原则,你需要采用Benjamin的回答:
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特性,缺点是你需要编写更多的代码,并且需要记住反映所有的promise以保持一致性。
我认为我的解决方案是显式和简单,但确实不够健壮。该接口无法保证您确切地知道promise是否成功或失败。
例如,您可能会遇到以下情况:
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
这不会被 a.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
的最终原因。
allSettled
方法,可以满足你的需求,而无需自己编写代码实现。 - DanPromise.all
会在 任何一个 promise 被拒绝时立即拒绝,因此您提出的习语不能保证所有 promise 都已解决。 - Jörg W Mittag