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

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

问题描述

假设我有一组 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.

由于 Promise.all 没有为此留下任何空间,在不使用承诺库的情况下,推荐的处理模式是什么?

Since Promise.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));

<小时>

更进一步,您可以编写一个如下所示的通用 catch 处理程序:


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

<小时>

第二种解决方案的好处是它的抽象和 DRY.缺点是你有更多的代码,你必须记住反映你所有的承诺,使事情保持一致.


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 捕获,所以

This won't get caught by a.catch, so

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

就我而言,我不需要知道错误或失败的原因——我只关心我是否拥有价值.我会让生成 promise 的函数负责记录特定的错误.

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——这就是我最终选择不使用 <代码>反射.

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天全站免登陆