处理Promise.all中的错误

时间:2015-05-21 00:46:27

标签: javascript promise es6-promise

我有一系列Promise,我用Promise.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返回它找到的第一个错误(忽略其余的),然后我就无法得到来自阵列中其余承诺的数据(没有错误)。

我尝试过像......这样的事情。

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

在.then之前放置.catch for Promise.all似乎是为了捕获原始promises中的任何错误,然后将整个数组返回到下一个.then

谢谢!

21 个答案:

答案 0 :(得分:126)

Promise.all全有或全无。一旦阵列中的所有承诺解决,它就会解析,或者只要一个拒绝它们就拒绝。换句话说,它可以使用所有已解析值的数组进行解析,也可以使用单个错误进行拒绝。

有些库有一些名为Promise.when的东西,我理解它会等待阵列中的所有承诺解析或拒绝,但我不熟悉它,并且它不在ES6中。

您的代码

我同意其他人的说法,你的解决方案应该有效。它应该使用可能包含成功值和错误对象的数组的数组来解析。在成功路径中传递错误对象是不寻常的,但假设您的代码期望它们,我认为没有问题。

我能想到它为什么不能解决问题的唯一原因。是因为你没有向我们展示代码的失败以及你没有看到任何关于这个的错误消息的原因是因为这个承诺链没有以最后的捕获终止(至于什么无论如何你都在向我们展示。)

我冒昧地将现有的连锁店&#34;从你的例子中,用catch来终止链。这可能不适合你,但对于读这篇文章的人来说,总是返回或终止链是重要的,或者潜在的错误,甚至编码错误都会被隐藏(这是我怀疑在这里发生的事情):< / p>

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

答案 1 :(得分:49)

新答案

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

OLD ANSWER

我们必须编写自定义Promise.all()。 这是我在项目中使用的解决方案。错误将作为正常结果返回。 在所有承诺完成后,我们可以过滤掉错误。

const Promise_all = promises => {
  return new Promise((resolve, reject) => {
    const results = [];
    let count = 0;
    promises.forEach((promise, idx) => {
      promise
        .catch(err => {
          return err;
        })
        .then(valueOrError => {
          results[idx] = valueOrError;
          count += 1;
          if (count === promises.length) resolve(results);
        });
    });
  });
};

const results = await Promise_all(promises)
const validResults = results.filter(result => !(result instanceof Error));

答案 2 :(得分:16)

要继续Promise.all循环(即使Promise拒绝),我写了一个名为executeAllPromises的实用程序函数。此实用程序函数返回具有resultserrors的对象。

这个想法是,你传递给executeAllPromises的所有Promise都将被包装成一个永远解决的新Promise。新的Promise解决了一个有2个点的阵列。第一个点保存解析值(如果有的话),第二个点保留错误(如果包装的Promise拒绝)。

作为最后一步,executeAllPromises累积包装的promises的所有值,并返回最终对象,其中包含results的数组和errors的数组。

以下是代码:

&#13;
&#13;
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}`);
});
&#13;
&#13;
&#13;

答案 3 :(得分: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();
});

答案 4 :(得分:5)

使用异步等待-

在这种情况下,一个异步函数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]

答案 5 :(得分:4)

对于使用ES8时遇到问题的用户,您可以使用async functions执行以下操作:

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

答案 6 :(得分: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) );
}

答案 7 :(得分:2)

Promise.allSettled with a filter

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

答案 8 :(得分:2)

Promise.allSettled

使用Promise.allSettled代替Promise,无论结果如何,都使用{{3}}来等待所有诺言兑现

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

答案 9 :(得分:2)

正如@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
  })

答案 10 :(得分:1)

possibleCrewPositions

答案 11 :(得分:1)

您是否考虑过Promise.prototype.finally()

它似乎完全可以按照您的意愿进行设计-在所有诺言都已兑现(解决/拒绝)后执行函数,无论某些诺言是否被拒绝。

来自MDN documentation

finally()方法非常有用,如果您希望在兑现承诺后进行一些处理或清理,而不管其结果如何。

finally()方法与调用.then(onFinally, onFinally)非常相似,但是有一些区别:

内联创建函数时,可以传递一次,而不必被强制声明两次或为其创建变量。

由于没有可靠的方法来确定诺言是否已兑现,因此最终回调将不会接收任何参数。正是这种用例适用于您不关心拒绝原因或实现价值,因此不需要提供它的情况。

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 ArchibaldPromise.all(promises.map(p => p.catch(() => undefined)));

使用以下解决方法

答案 12 :(得分: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)
      }
    }
  )
}

答案 13 :(得分:0)

您始终可以将诺言返回函数包装为能够捕获故障并返回约定值(例如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'};
    }

}

答案 14 :(得分: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)

答案 15 :(得分: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
  }

答案 16 :(得分: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;

答案 17 :(得分:0)

ES2020引入了Promise类型的新方法:Promise.allSettled()
Promise.allSettled在所有输入的诺言都已兑现时向您发出信号,这意味着它们已实现或被拒绝。如果您不在乎承诺的状态,而只是想知道工作何时完成,无论它是否成功,这都是很有用的。

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

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

在v8博客文章https://v8.dev/features/promise-combinators

中了解更多信息

答案 18 :(得分:-1)

您可以使用新的for-await语法编写自己的PromiseAll函数,以处理错误。

源数据

const p1 = Promise.reject('wrong!')
const p2 = Promise.resolve(444)
const p3 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, 'error!')
})
const p4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'nice!')
})

const promisesArr = [p1, p2, p3, p4]

我们可以编写一个遍历数组的函数,并将完成的承诺的状态写入结果。

功能和执行

const safetyPromiseAll = async promisesArr => {
  const results = [];
  for (const promise of promisesArr) {
    const promiseResult = {};
    try {
      // in this block we execute our promise 
      // and if succesfull write appropriate status/value
      promiseResult.data = await promise; 
      promiseResult.status = 'success';
    } catch (e) {
      // in this block we handle promise reject
      promiseResult.data = e;
      promiseResult.status = 'error';
    } finally {
      // whatever the outcome we push result in array
      results.push(promiseResult);
    }
  }
  return results;
};

safetyPromiseAll.then(console.log)

结果

[
  {
    "data": "wrong!",
    "status": "error"
  },
  {
    "data": 444,
    "status": "success"
  },
  {
    "data": "error!",
    "status": "error"
  },
  {
    "data": "nice!",
    "status": "success"
  }
]

答案 19 :(得分:-1)

这就是Promise.all的设计方式。如果单个承诺reject(),则整个方法立即失败。

有些用例可能会让Promise.all允许承诺失败。要实现这一点,请不要在您的承诺中使用任何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);
        });
    });
}

答案 20 :(得分:-7)

我写了一个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))

--------------------------------

老坏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'}