等到所有诺言都完成,即使有些诺言被拒绝

本文翻译自:Wait until all promises complete even if some rejected

Let's say I have a set of Promise s that are making network requests, of which one will fail: 假设我有一组Promise正在发出网络请求,其中一个会失败:

// 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. 我想优雅地处理网络故障。

Since Promises.all doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library? 由于Promises.all对此没有任何余地,因此在不使用promises库的情况下,推荐的处理方式是什么?


#1楼

参考:https://stackoom.com/question/27qxl/等到所有诺言都完成-即使有些诺言被拒绝


#2楼

I don't know which promise library you are using, but most have something like allSettled . 我不知道您正在使用哪个Promise库,但是大多数库都具有allSettled之类的东西

Edit: Ok since you want to use plain ES6 without external libraries, there is no such method. 编辑:好的,因为您想使用没有外部库的普通ES6,所以没有这种方法。

In other words: You have to loop over your promises manually and resolve a new combined promise as soon as all promises are settled. 换句话说:您必须手动遍历您的诺言,并在所有诺言被兑现后立即解决新的合并诺言。


#3楼

Sure, you just need a reflect : 当然,您只需要reflect

const reflect = p => p.then(v => ({v, status: "fulfilled" }),
                            e => ({e, status: "rejected" }));

reflect(promise).then((v => {
    console.log(v.status);
});

Or with ES5: 或使用ES5:

function reflect(promise){
    return promise.then(function(v){ return {v:v, status: "fulfilled" }},
                        function(e){ return {e:e, status: "rejected" }});
}


reflect(promise).then(function(v){
    console.log(v.status);
});

Or in your example: 或在您的示例中:

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

Promise.all(arr.map(reflect)).then(function(results){
    var success = results.filter(x => x.status === "fulfilled");
});

#4楼

I really like Benjamin's answer, and how he basically turns all promises into always-resolving-but-sometimes-with-error-as-a-result ones. 我真的很喜欢本杰明的回答,以及他基本上是如何将所有诺言变成总是能解决但有时会因结果而出错的。 :) :)
Here's my attempt at your request just in case you were looking for alternatives. 如果您正在寻找替代品,这是我对您的要求的尝试。 This method simply treats errors as valid results, and is coded similar to Promise.all otherwise: 此方法只是将错误视为有效结果,其编码类似于Promise.all

Promise.settle = function(promises) {
  var results = [];
  var done = promises.length;

  return new Promise(function(resolve) {
    function tryResolve(i, v) {
      results[i] = v;
      done = done - 1;
      if (done == 0)
        resolve(results);
    }

    for (var i=0; i<promises.length; i++)
      promises[i].then(tryResolve.bind(null, i), tryResolve.bind(null, i));
    if (done == 0)
      resolve(results);
  });
}

#5楼

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. 解决此问题的明确方法是简单地对内部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));

Taking this one step further, you could write a generic catch handler that looks like this: 更进一步,您可以编写一个通用的catch处理程序,如下所示:

const catchHandler = error => ({ payload: error, resolved: false });

then you can do 那你就可以

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

where it now looks like 现在看起来像什么

> 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. 第二个解决方案的好处是抽象和DRY。 The downside is you have more code, and you have to remember to reflect all your promises to make things consistent. 不利的一面是您拥有更多的代码,并且必须记住要体现所有使事情保持一致的承诺。

I would characterize my solution as explicit and KISS, but indeed less robust. 我将我的解决方案描述为显式和KISS,但确实不够可靠。 The interface doesn't guarantee that you know exactly whether the promise succeeded or failed. 该界面不能保证您确切知道承诺是成功还是失败。

For example you might have this: 例如,您可能有以下内容:

const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));

This won't get caught by a.catch , so 这不会被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). 如果这很重要,那么您将需要实施和接口以跟踪是否成功(这reflect成功)。

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. 我将让生成promise的函数担心记录特定错误。

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. 这样,应用程序的其余部分可以根据需要忽略其错误,并在需要时将其视为未定义的值。

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 . 我希望我的高级函数能够安全地失败,并且不必担心其依赖项失败的原因的细节,而且在必须进行权衡时,我也更喜欢KISS而不是DRY,这最终就是为什么我选择不使用reflect


#6楼

var err;
Promise.all([
    promiseOne().catch(function(error) { err = error;}),
    promiseTwo().catch(function(error) { err = error;})
]).then(function() {
    if (err) {
        throw err;
    }
});

The Promise.all will swallow any rejected promise and store the error in a variable, so it will return when all of the promises have resolved. Promise.all将吞下所有被拒绝的promise,并将错误存储在变量中,因此当所有promise被解决后,它将返回。 Then you can re-throw the error out, or do whatever. 然后,您可以将错误重新抛出,或者执行任何操作。 In this way, I guess you would get out the last rejection instead of the first one. 这样,我想您将获得最后一个拒绝而不是第一个拒绝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值