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

我知道这个问题有很多答案,我确信一定(如果不是全部)是正确的。 然而,我很难理解这些答案的逻辑/流程。

因此,我查看了Promise.all()的原始实现,并尝试模仿该逻辑-除了如果一个Promise失败不停止执行之外。

  public promiseExecuteAll(promisesList: Promise<any>[] = []): Promise<{ data: any, isSuccess: boolean }[]>
  {
    let promise: Promise<{ data: any, isSuccess: boolean }[]>;

    if (promisesList.length)
    {
      const result: { data: any, isSuccess: boolean }[] = [];
      let count: number = 0;

      promise = new Promise<{ data: any, isSuccess: boolean }[]>((resolve, reject) =>
      {
        promisesList.forEach((currentPromise: Promise<any>, index: number) =>
        {
          currentPromise.then(
            (data) => // Success
            {
              result[index] = { data, isSuccess: true };
              if (promisesList.length <= ++count) { resolve(result); }
            },
            (data) => // Error
            {
              result[index] = { data, isSuccess: false };
              if (promisesList.length <= ++count) { resolve(result); }
            });
        });
      });
    }
    else
    {
      promise = Promise.resolve([]);
    }

    return promise;
  }

解释: -循环输入的许诺列表并执行每个许诺。 —无论Promise是被解决还是被拒绝:将Promise的结果按照索引保存为结果数组。还保存解析/拒绝状态(isSuccess)。 -一旦所有承诺完成,将一个承诺与所有其他承诺的结果一起返回。

使用示例:

const p1 = Promise.resolve("OK");
const p2 = Promise.reject(new Error(":-("));
const p3 = Promise.resolve(1000);

promiseExecuteAll([p1, p2, p3]).then((data) => {
  data.forEach(value => console.log(`${ value.isSuccess ? 'Resolve' : 'Reject' } >> ${ value.data }`));
});

/* Output: 
Resolve >> OK
Reject >> :-(
Resolve >> 1000
*/

的承诺。所有这些都使用现代异步/等待方法

const promise1 = //...
const promise2 = //...

const data = await Promise.all([promise1, promise2])

const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]

我只是想要一个完全复制ES2020行为的填充,因为我被锁定在比12.9更早的节点版本(当Promise。allsettle出现了),不幸的是。所以不管怎样,这是我的版本:

const settle = (promise) => (promise instanceof Promise) ?
  promise.then(val => ({ value: val, status: "fulfilled" }),
               err => ({ reason: err, status: "rejected" })) :
  { value: promise, status: 'fulfilled' };

const allSettled = async (parr) => Promise.all(parr.map(settle));

它处理承诺值和非承诺值的混合数组,就像ES版本一样。它返回与原生版本相同的{status, value/reason}对象数组。

你可以通过同步执行器nsynjs按顺序执行你的逻辑。它将在每个承诺上暂停,等待解析/拒绝,并将解析的结果分配给data属性,或者抛出异常(用于处理您将需要try/catch块)。这里有一个例子:

function synchronousCode() { function myFetch(url) { try { return window.fetch(url).data; } catch (e) { return {status: 'failed:'+e}; }; }; var arr=[ myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"), myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"), myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js") ]; console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status); }; nsynjs.run(synchronousCode,{},function(){ console.log('done'); }); <script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>