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

假设我想要等到所有这些都完成,不管是否有一个失败了。可能有一个资源的网络错误,我可以没有,但如果我能得到,我想在继续之前。我想优雅地处理网络故障。

因为承诺。所有这些都没有留下任何空间,在不使用承诺库的情况下,处理这个问题的推荐模式是什么?


当前回答

这应该与Q的做法一致:

if(!Promise.allSettled) {
    Promise.allSettled = function (promises) {
        return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
            state: 'fulfilled',
            value: v,
        }), r => ({
            state: 'rejected',
            reason: r,
        }))));
    };
}

其他回答

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

的承诺。All将吞下任何被拒绝的承诺,并将错误存储在一个变量中,因此当所有承诺都已解决时,它将返回。然后可以重新抛出错误,或者做其他事情。这样,我猜你会得到最后一次拒绝,而不是第一次。

与其拒绝,不如用一个对象来解决。 当你实现承诺时,你可以这样做

Const promise = arg => { 返回新的承诺((resolve, reject) => { setTimeout(() => { 尝试{ If (arg != 2) 返回解析({success: true, data: arg}); 其他的 抛出新的错误(arg) }捕捉(e) { 返回解析({success: false, error: e, data: arg}) } }, 1000); }) } Promise.all([1、2、3、4、5)。Map (e => promise(e)))。然后(d => console.log(d))

我也遇到过同样的问题,我用下面的方法解决了这个问题:

const fetch = (url) => {
  return node-fetch(url)
    .then(result => result.json())
    .catch((e) => {
      return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
    });
};

tasks = [fetch(url1), fetch(url2) ....];

Promise.all(tasks).then(......)

那样的话,答应我。所有人都在等待每一个承诺都将进入解决或拒绝的状态。

有了这个解决方案,我们以一种非阻塞的方式“停止捕获执行”。事实上,我们并没有停止任何事情,我们只是返回处于挂起状态的Promise,当它在超时后被解决时,它会返回另一个Promise。

这是我的custom settdpromiseall ()

const settledPromiseAll = function(promisesArray) {
  var savedError;

  const saveFirstError = function(error) {
    if (!savedError) savedError = error;
  };
  const handleErrors = function(value) {
    return Promise.resolve(value).catch(saveFirstError);
  };
  const allSettled = Promise.all(promisesArray.map(handleErrors));

  return allSettled.then(function(resolvedPromises) {
    if (savedError) throw savedError;
    return resolvedPromises;
  });
};

与Promise.all相比

如果所有的承诺都得到了解决,那么它的性能与标准承诺完全相同。 如果多个promise中有一个被拒绝,它返回第一个被拒绝的promise,与标准的promise大致相同,但不同的是,它等待所有的promise都被解决/拒绝。

为了勇敢,我们可以改变。

(function() {
  var stdAll = Promise.all;

  Promise.all = function(values, wait) {
    if(!wait)
      return stdAll.call(Promise, values);

    return settledPromiseAll(values);
  }
})();

小心。一般来说,我们从不改变内置程序,因为这可能会破坏其他不相关的JS库,或者与未来对JS标准的更改发生冲突。

我的承诺是向后兼容的承诺。并扩展其功能。

开发标准的人——为什么不将其包含到新的Promise标准中呢?

更新,你可能需要使用内置的本机promise . allsettle:

Promise.allSettled([promise]).then(([result]) => {
   //reach here regardless
   // {status: "fulfilled", value: 33}
});

作为一个有趣的事实,下面的答案是将该方法添加到语言中的现有技术:]


当然,你只需要反思一下:

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

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

或者使用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);
});

或者在你的例子中:

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