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

2022-08-29 23:03:00

假设我有一组正在发出网络请求的 s,其中一个会失败: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没有为此留下任何空间,那么在不使用promises库的情况下处理此问题的建议模式是什么?


答案 1

更新,你可能想使用内置的原生 Promise.allSettled

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

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


当然,你只需要一个:reflect

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

答案 2

类似的答案,但对于ES6来说可能更习惯用语:

const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);

Promise.all([a, b, c].map(p => p.catch(e => e)))
  .then(results => console.log(results)) // 1,Error: 2,3
  .catch(e => console.log(e));


const console = { log: msg => div.innerHTML += msg + "<br>"};
<div id="div"></div>

根据返回值的类型,通常可以很容易地区分错误(例如,用于“不关心”,用于普通的非对象值等)。undefinedtypeofresult.messageresult.toString().startsWith("Error:")