等到所有的诺言完成,即使有些诺言被拒绝 [英] Wait until all promises complete even if some rejected

查看:72
本文介绍了等到所有的诺言完成,即使有些诺言被拒绝的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一组 Promise 正在发出网络请求,但其中一个失败:

Let's say I have a set of Promises that are making network requests, of which one will fail:

// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr)
  .then(res => console.log('success', res))
  .catch(err => console.log('error', err)) // This is executed   

假设我想等到所有这些完成为止,无论是否失败。我可能无法使用的资源可能会出现网络错误,但是如果可以得到,我会在继续之前要这样做。我想优雅地处理网络故障。

Let's say I want to wait until all of these have finished, regardless of if one has failed. There might be a network error for a resource that I can live without, but which if I can get, I want before I proceed. I want to handle network failures gracefully.

由于 Promises.all 对此没有任何余地,建议采用哪种处理模式,而不使用promises库?

Since Promises.all doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library?

推荐答案

Benjamin的回答为解决此问题提供了一个很好的抽象,但是我希望减少抽象的解决方案。解决此问题的明确方法是简单地对内部promise调用 .catch ,并从其回调中返回错误。

Benjamin's answer offers a great abstraction for solving this issue, but I was hoping for a less abstracted solution. The explicit way to to resolve this issue is to simply call .catch on the internal promises, and return the error from their callback.

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));






再走一步,您可以写一个通用捕获处理程序,如下所示:


Taking this one step further, you could write a generic catch handler that looks like this:

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 } ]

问题是被捕获的值与非被捕获的值将具有不同的接口,因此要清理它,您可以执行以下操作:

The problem with this is that the caught values will have a different interface than the non-caught values, so to clean this up you might do something like:

const successHandler = result => ({ payload: result, resolved: true });

现在您可以执行以下操作:

So now you can do this:

> 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!' ]

然后将其保持干燥,您会得到本杰明的回答:

Then to keep it DRY, you get to Benjamin's answer:

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!' ]






第二种解决方案的好处是抽象和干燥。


The benefits of the second solution are that its abstracted and DRY. The downside is you have more code, and you have to remember to reflect all your promises to make things consistent.

我会把我的解决方案描述为显式和KISS,但实际上不那么健壮。不能保证您确切知道诺言是成功还是失败。

I would characterize my solution as explicit and KISS, but indeed less robust. The interface doesn't guarantee that you know exactly whether the promise succeeded or failed.

例如,您可能会这样:

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 可以做到)。

There's no way to tell which one was fatal and which was wasn't. If that's important then you're going to want to enforce and interface that tracks whether it was successful or not (which reflect does).

如果您只想优雅地处理错误,则可以将错误视为未定义的值:

If you just want to handle errors gracefully, then you can just treat errors as undefined values:

> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
    .then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]

对于我来说,我不需要知道错误或失败的方式,我只是在乎是否有此价值。

In my case, I don't need to know the error or how it failed--I just care whether I have the value or not. I'll let the function that generates the promise worry about logging the specific error.

const apiMethod = () => fetch()
  .catch(error => {
    console.log(error.message);
    throw error;
  });

这样,应用程序的其余部分可以根据需要忽略其错误,并将其视为

That way, the rest of the application can ignore its error if it wants, and treat it as an undefined value if it wants.

我希望我的高级函数安全地失败,而不必担心其依赖项失败的原因的细节,并且我更喜欢KISS而不是DRY我必须权衡一下,这就是为什么我最终选择不使用 reflect

I want my high level functions to fail safely and not worry about the details on why its dependencies failed, and I also prefer KISS to DRY when I have to make that tradeoff--which is ultimately why I opted to not use reflect.

这篇关于等到所有的诺言完成,即使有些诺言被拒绝的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆