等到所有ES6承诺完成,甚至拒绝承诺 [英] Wait until all ES6 promises complete, even rejected promises

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

问题描述

假设我有一组提出网络请求的承诺,其中一个会失败:

Lets 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   

让我们说我要等到所有这些都完成了,无论如何如果一个人失败了对于我可以没有的资源,可能存在网络错误,但如果我可以获得,我希望在继续之前。我想优雅地处理网络故障。

Lets 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 不会留下任何空间,处理此问题的推荐模式是什么,不使用承诺库?

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

推荐答案

本杰明的回答为解决这个问题提供了很好的抽象,但我希望得到一个不那么抽象的解决方案。解决此问题的明确方法是在内部promises上调用 .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));



< hr>

更进一步,您可以编写一个通用的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 });

现在你可以这样做:

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

没有办法告诉哪一个是致命的,哪个不是如果这很重要,那么你将要强制执行和界面跟踪它是否成功(反映确实如此)。

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干我必须做出权衡 - 这最终是我选择不使用反映的原因。

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.

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

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