365

我有一系列要解决的 PromisePromise.all(arrayOfPromises);

我继续继续承诺链。看起来像这样

existingPromiseChain = existingPromiseChain.then(function() {
  var arrayOfPromises = state.routes.map(function(route){
    return route.handler.promiseHandler();
  });
  return Promise.all(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
  // do stuff with my array of resolved promises, eventually ending with a res.send();
});

我想添加一个catch语句来处理单个promise,以防它出错,但是当我尝试时,Promise.all返回它发现的第一个错误(忽略其余的),然后我无法从promise的其余部分获取数据数组(没有错误)。

我试过做类似..

existingPromiseChain = existingPromiseChain.then(function() {
      var arrayOfPromises = state.routes.map(function(route){
        return route.handler.promiseHandler()
          .then(function(data) {
             return data;
          })
          .catch(function(err) {
             return err
          });
      });
      return Promise.all(arrayOfPromises)
    });

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
      // do stuff with my array of resolved promises, eventually ending with a res.send();
});

但这并不能解决。

谢谢!

--

编辑:

下面的答案完全正确,由于其他原因,代码被破坏了。如果有人感兴趣,这是我最终得到的解决方案......

Node Express 服务器链

serverSidePromiseChain
    .then(function(AppRouter) {
        var arrayOfPromises = state.routes.map(function(route) {
            return route.async();
        });
        Promise.all(arrayOfPromises)
            .catch(function(err) {
                // log that I have an error, return the entire array;
                console.log('A promise failed to resolve', err);
                return arrayOfPromises;
            })
            .then(function(arrayOfPromises) {
                // full array of resolved promises;
            })
    };

API 调用(route.async 调用)

return async()
    .then(function(result) {
        // dispatch a success
        return result;
    })
    .catch(function(err) {
        // dispatch a failure and throw error
        throw err;
    });

.catch将for放在 thePromise.all之前.then似乎已经达到了从原始承诺中捕获任何错误的目的,然后将整个数组返回到下一个.then

谢谢!

4

20 回答 20

247

Promise.all是全部或全部。一旦数组中的所有承诺都解决了,它就会解决,或者一旦其中一个被拒绝,它就会被拒绝。换句话说,它要么使用所有已解析值的数组解析,要么以单个错误拒绝。

一些库有一个叫做 的东西Promise.when,我理解它会等待数组中的所有承诺来解决或拒绝,但我不熟悉它,它不在 ES6 中。

你的代码

我在这里同意其他人的观点,即您的修复应该有效。它应该使用一个可能包含成功值和错误对象的数组来解析。在成功路径中传递错误对象是不寻常的,但假设您的代码期待它们,我认为它没有问题。

我能想到它为什么“无法解决”的唯一原因是它在您没有向我们展示的代码中失败,而您没有看到任何有关此的错误消息的原因是因为这个承诺链没有以 final 终止抓住(就您向我们展示的内容而言)。

我冒昧地从您的示例中提取了“现有链”,并用一个捕获终止了该链。这可能不适合您,但对于阅读本文的人来说,始终返回或终止链很重要,否则潜在的错误,甚至编码错误都会被隐藏(我怀疑这里发生了这种情况):

Promise.all(state.routes.map(function(route) {
  return route.handler.promiseHandler().catch(function(err) {
    return err;
  });
}))
.then(function(arrayOfValuesOrErrors) {
  // handling of my array containing values and/or errors. 
})
.catch(function(err) {
  console.log(err.message); // some coding error in handling happened
});
于 2015-05-21T15:27:28.850 回答
214

新答案

const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));

未来的承诺 API

于 2017-09-03T14:46:27.160 回答
78

ES2020为类型引入了新方法PromisePromise.allSettled().

Promise.allSettled当所有输入承诺都已解决时,会给你一个信号,这意味着它们要么被履行,要么被拒绝。这在您不关心承诺状态的情况下很有用,您只想知道工作何时完成,而不管它是否成功。

(async function() {
  const promises = [
    fetch('//api.stackexchange.com/2.2'), // succeeds
    fetch('/this-will-fail') // fails
  ];

  const result = await Promise.allSettled(promises);
  console.log(result.map(promise => promise.status));
  // ['fulfilled', 'rejected']
})();

v8 博客文章中阅读更多内容。

于 2019-12-21T13:13:06.227 回答
24

为了继续Promise.all循环(即使 Promise 拒绝),我编写了一个名为executeAllPromises. 此实用程序函数返回一个带有results和的对象errors

这个想法是,您传递给的所有executeAllPromisesPromise 都将被包装到一个新的 Promise 中,该 Promise 将始终解决。新的 Promise 使用具有 2 个点的数组解析。第一个点保存解析值(如果有),第二个点保存错误(如果包装的 Promise 拒绝)。

作为最后一步,executeAllPromises累积包装承诺的所有值并返回带有数组 forresults和数组 for的最终对象errors

这是代码:

function executeAllPromises(promises) {
  // Wrap all Promises in a Promise that will always "resolve"
  var resolvingPromises = promises.map(function(promise) {
    return new Promise(function(resolve) {
      var payload = new Array(2);
      promise.then(function(result) {
          payload[0] = result;
        })
        .catch(function(error) {
          payload[1] = error;
        })
        .then(function() {
          /* 
           * The wrapped Promise returns an array:
           * The first position in the array holds the result (if any)
           * The second position in the array holds the error (if any)
           */
          resolve(payload);
        });
    });
  });

  var errors = [];
  var results = [];

  // Execute all wrapped Promises
  return Promise.all(resolvingPromises)
    .then(function(items) {
      items.forEach(function(payload) {
        if (payload[1]) {
          errors.push(payload[1]);
        } else {
          results.push(payload[0]);
        }
      });

      return {
        errors: errors,
        results: results
      };
    });
}

var myPromises = [
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.reject(new Error('3')),
  Promise.resolve(4),
  Promise.reject(new Error('5'))
];

executeAllPromises(myPromises).then(function(items) {
  // Result
  var errors = items.errors.map(function(error) {
    return error.message
  }).join(',');
  var results = items.results.join(',');
  
  console.log(`Executed all ${myPromises.length} Promises:`);
  console.log(`— ${items.results.length} Promises were successful: ${results}`);
  console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});

于 2017-01-06T14:28:54.387 回答
19

正如@jib 所说,

Promise.all是全部或全部。

不过,您可以控制某些“允许”失败的承诺,我们希望继续.then.

例如。

  Promise.all([
    doMustAsyncTask1,
    doMustAsyncTask2,
    doOptionalAsyncTask
    .catch(err => {
      if( /* err non-critical */) {
        return
      }
      // if critical then fail
      throw err
    })
  ])
  .then(([ mustRes1, mustRes2, optionalRes ]) => {
    // proceed to work with results
  })
于 2019-02-14T21:08:35.050 回答
18

Promise.all 已解决

而不是 Promise.all 使用Promise.allSettled等待所有承诺解决,无论结果如何

let p1 = new Promise(resolve => resolve("result1"));
let p2 = new Promise( (resolve,reject) => reject('some troubles') );
let p3 = new Promise(resolve => resolve("result3"));

// It returns info about each promise status and value
Promise.allSettled([p1,p2,p3]).then(result=> console.log(result));

填充物

if (!Promise.allSettled) {
  const rejectHandler = reason => ({ status: 'rejected', reason });
  const resolveHandler = value => ({ status: 'fulfilled', value });

  Promise.allSettled = function (promises) {
    const convertedPromises = promises
      .map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
    return Promise.all(convertedPromises);
  };
}

于 2020-06-29T09:00:24.210 回答
7

使用异步等待 -

这里有一个异步函数 func1 返回一个解析的值,而 func2 在这种情况下抛出错误并返回 null,我们可以按照我们想要的方式处理它并相应地返回。

const callingFunction  = async () => {
    const manyPromises = await Promise.all([func1(), func2()]);
    console.log(manyPromises);
}


const func1 = async () => {
    return 'func1'
}

const func2 = async () => {
    try {
        let x;
        if (!x) throw "x value not present"
    } catch(err) {
       return null
    }
}

callingFunction();

输出是 - [ 'func1', null ]

于 2019-03-18T07:54:11.287 回答
6

如果您使用 q 库https://github.com/kriskowal/q 它具有可以解决此问题的 q.allSettled() 方法,您可以根据其状态处理每个承诺,无论是完整还是拒绝,所以

existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
  return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
   if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
     //do somthing
   } else {
     // do something else
   }
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
于 2016-06-19T14:36:33.493 回答
4

对于那些使用 ES8 的人来说,您可以使用异步函数执行以下操作:

var arrayOfPromises = state.routes.map(async function(route){
  try {
    return await route.handler.promiseHandler();
  } catch(e) {
    // Do something to handle the error.
    // Errored promises will return whatever you return here (undefined if you don't return anything).
  }
});

var resolvedPromises = await Promise.all(arrayOfPromises);
于 2018-09-17T22:32:11.193 回答
3

我们可以在单个 Promise 级别处理拒绝,因此当我们在结果数组中获取结果时,被拒绝的数组索引将为undefined. 我们可以根据需要处理这种情况,并使用剩余的结果。

这里我拒绝了第一个承诺,所以它是未定义的,但我们可以使用第二个承诺的结果,它在索引 1 处。

const manyPromises = Promise.all([func1(), func2()]).then(result => {
    console.log(result[0]);  // undefined
    console.log(result[1]);  // func2
});

function func1() {
    return new Promise( (res, rej) => rej('func1')).catch(err => {
        console.log('error handled', err);
    });
}

function func2() {
    return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );
}

于 2018-09-03T14:47:55.897 回答
3

你考虑过Promise.prototype.finally()吗?

它似乎旨在完全按照您的意愿行事 - 一旦所有承诺都已解决(已解决/拒绝),则执行一个函数,而不管某些承诺是否被拒绝。

MDN 文档

finally()如果您想在承诺完成后进行一些处理或清理,无论其结果如何,该方法都会很有用。

finally()方法与调用非常相似,.then(onFinally, onFinally)但是有几个不同之处:

在创建内联函数时,您可以传递一次,而不是被迫声明它两次,或者为它创建一个变量。

finally 回调不会收到任何参数,因为没有可靠的方法来确定承诺是否被履行或拒绝。这个用例正是在你不关心拒绝原因或履行价值的时候,所以没有必要提供它。

不同Promise.resolve(2).then(() => {}, () => {})(将用 undefined 解决),Promise.resolve(2).finally(() => {})将用 2 解决。类似地,不同Promise.reject(3).then(() => {}, () => {})(将用 undefined 完成),Promise.reject(3).finally(() => {})将用 3 拒绝。

== 后备 ==

如果您的 JavaScript 版本不支持Promise.prototype.finally(),您可以使用Jake Archibald的解决方法:Promise.all(promises.map(p => p.catch(() => undefined)));

于 2018-12-13T00:08:39.563 回答
3

Promise.all 用过滤器解决

const promises = [
  fetch('/api-call-1'),
  fetch('/api-call-2'),
  fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.

const resultFilter = (result, error) => result.filter(i => i.status === (!error ? 'fulfilled' : 'rejected')).map(i => (!error ? i.value : i.reason));

const result = await Promise.allSettled(promises);

const fulfilled = resultFilter(result); // all fulfilled results
const rejected = resultFilter(result, true); // all rejected results
于 2021-04-27T12:57:34.050 回答
2
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
let sum = 0;
let promiseErrorArr = [];

Promise.allSettled(promises)
.then((results) => {
          results.forEach(result => {
            if (result.status === "rejected") {
              sum += 1;
              promiseErrorArr.push(result)
            }
          })
    return ( (sum>0) ? promiseFailed() : promisePassed())
})

function promiseFailed(){
  console.log('one or all failed!')
  console.log(promiseErrorArr)
}

function promisePassed(){
  console.log('all passed!')
}

// expected output:
// "one or all failed!"
// Array [Object { status: "rejected", reason: "foo" }]
于 2021-05-10T19:40:15.700 回答
0

或者,如果您遇到这样一种情况,即当出现一次失败时您并不特别关心已解决的承诺的值,但您仍然希望它们运行,您可以做这样的事情,这将在以下情况下正常解决承诺当它们中的任何一个失败时,它们都会成功并拒绝失败的承诺:

function promiseNoReallyAll (promises) {
  return new Promise(
    async (resolve, reject) => {
      const failedPromises = []

      const successfulPromises = await Promise.all(
        promises.map(
          promise => promise.catch(error => {
            failedPromises.push(error)
          })
        )
      )

      if (failedPromises.length) {
        reject(failedPromises)
      } else {
        resolve(successfulPromises)
      }
    }
  )
}
于 2019-04-30T21:44:21.070 回答
0

你总是可以包装你的promise返回函数,让它们捕获失败并返回一个约定的值(例如error.message),所以异常不会一直滚动到Promise.all函数并禁用它。

async function resetCache(ip) {

    try {

        const response = await axios.get(`http://${ip}/resetcache`);
        return response;

    }catch (e) {

        return {status: 'failure', reason: 'e.message'};
    }

}
于 2019-07-15T07:37:59.807 回答
0

我找到了一种方法(解决方法)来做到这一点而不使其同步。

因此,正如之前提到Promise.all的,一无所有。

所以...使用封闭的承诺来捕捉和强制解决。


      let safePromises = originalPrmises.map((imageObject) => {
            return new Promise((resolve) => {
              // Do something error friendly
              promise.then(_res => resolve(res)).catch(_err => resolve(err))
            })
        })
    })

    // safe
    return Promise.all(safePromises)
于 2019-11-17T18:37:56.917 回答
0

您需要知道如何识别结果中的错误。如果您没有标准的预期错误,我建议您对 catch 块中的每个错误进行转换,使其在您的结果中可识别。

try {
  let resArray = await Promise.all(
    state.routes.map(route => route.handler.promiseHandler().catch(e => e))
  );

  // in catch(e => e) you can transform your error to a type or object
  // that makes it easier for you to identify whats an error in resArray
  // e.g. if you expect your err objects to have e.type, you can filter
  // all errors in the array eg
  // let errResponse = resArray.filter(d => d && d.type === '<expected type>')
  // let notNullResponse = resArray.filter(d => d)

  } catch (err) {
    // code related errors
  }
于 2019-11-19T00:54:13.023 回答
0

这不是记录错误的最佳方式,但您始终可以将所有内容设置为 promiseAll 的数组,并将结果结果存储到新变量中。

如果您使用 graphQL,则无论如何都需要对响应进行后处理,如果它找不到正确的引用,它将使应用程序崩溃,从而缩小问题所在

const results = await Promise.all([
  this.props.client.query({
    query: GET_SPECIAL_DATES,
  }),
  this.props.client.query({
    query: GET_SPECIAL_DATE_TYPES,
  }),
  this.props.client.query({
    query: GET_ORDER_DATES,
  }),
]).catch(e=>console.log(e,"error"));
const specialDates = results[0].data.specialDates;
const specialDateTypes = results[1].data.specialDateTypes;
const orderDates = results[2].data.orders;
于 2019-12-10T15:46:34.007 回答
-1

Promise.all就是设计的工作方式。如果一个 promisereject()的,整个方法立即失败。

在某些用例中,人们可能希望Promise.all允许 Promise 失败。要做到这一点,不要reject()在你的承诺中使用任何陈述。但是,为了确保您的应用程序/脚本不会在任何单个基础承诺从未得到响应的情况下冻结,您需要对其设置超时。

function getThing(uid,branch){
    return new Promise(function (resolve, reject) {
        xhr.get().then(function(res) {
            if (res) {
                resolve(res);
            } 
            else {
                resolve(null);
            }
            setTimeout(function(){reject('timeout')},10000)
        }).catch(function(error) {
            resolve(null);
        });
    });
}
于 2018-06-10T04:37:34.103 回答
-8

我写了一个npm库来处理这个问题更漂亮。 https://github.com/wenshin/promiseallend

安装

npm i --save promiseallend

2017-02-25 新的 api,它不是违背承诺的原则

const promiseAllEnd = require('promiseallend');

const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};

// input promises with array
promiseAllEnd(promises, {
    unhandledRejection(error, index) {
        // error is the original error which is 'error'.
        // index is the index of array, it's a number.
        console.log(error, index);
    }
})
    // will call, data is `[1, undefined, 2]`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// input promises with object
promiseAllEnd(promisesObj, {
    unhandledRejection(error, prop) {
        // error is the original error.
        // key is the property of object.
        console.log(error, prop);
    }
})
    // will call, data is `{k1: 1, k3: 2}`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
    // won't call
    .then(data => console.log(data))
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
    // will call, data is `[1, undefined, 2]`.
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

————————————————————————————————————</p>

老坏的api,别用了!

let promiseAllEnd = require('promiseallend');

// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
    .then(data => console.log(data)) // [1, undefined, 2]
    .catch(error => console.log(error.errorsByKey)) // {1: 'error'}

// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
    .then(data => console.log(data)) // {k1: 1, k3: 2}
    .catch(error => console.log(error.errorsByKey)) // {k2: 'error'}
于 2016-05-15T06:48:45.470 回答