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

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

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


当前回答

有一个完成的提议,一个函数,可以完成这个本机,在香草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"
  }
]

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

其他回答

这是我的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标准中呢?

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

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。

我只是想要一个完全复制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}对象数组。

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

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:")等