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

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

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


当前回答

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

因此,我查看了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
*/

其他回答

有一个完成的提议,一个函数,可以完成这个本机,在香草Javascript:承诺。allsettle已经进入了第4阶段,在ES2020中正式发布,并在所有现代环境中实现。它和另一个答案中的反射函数很相似。这里有一个例子,来自提案页面。以前,你必须这样做:

function reflect(promise) {
  return promise.then(
    (v) => {
      return { status: 'fulfilled', value: v };
    },
    (error) => {
      return { status: 'rejected', reason: error };
    }
  );
}

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.all(promises.map(reflect));
const successfulPromises = results.filter(p => p.status === 'fulfilled');

使用的承诺。取而代之的是,上述内容将等价于:

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');

那些使用现代环境的人不需要任何库就可以使用这种方法。在这些情况下,下面的代码段应该可以正常运行:

Promise.allSettled ([ Promise.resolve (' a '), Promise.reject (b) ]) 不要犹豫(console.log);

输出:

[
  {
    "status": "fulfilled",
    "value": "a"
  },
  {
    "status": "rejected",
    "reason": "b"
  }
]

对于较老的浏览器,这里有一个符合规范的填充。

更新,你可能需要使用内置的本机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");
});

我不知道你在使用哪个承诺库,但大多数都有类似allsettle的东西。

编辑:好的,因为你想使用普通的ES6而没有外部库,没有这样的方法。

换句话说:您必须手动遍历承诺,并在所有承诺解决后立即解决新的组合承诺。

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

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

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

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

类似的答案,但更适合ES6:

const a = Promise.resolve(1); const b =承诺。拒绝(新的错误(2)); const c = Promise.resolve(3); 的承诺。[a, b, c]。(p => p.catch(e => e))) .then(results => console.log(results)) // 1,错误:2,3 .catch(e => console.log(e)); const console = {log: msg => div.innerHTML += msg + "<br>"}; < div id = " div " > < / div >

根据返回值的类型,通常可以很容易地区分错误(例如,用undefined表示“don't care”,用typeof表示普通的非对象值,用result。result.toString(). startswith ("Error:")等