【问题标题】:How can I use promises to catch errors when they might not be wrapped in a promise?当错误可能未包含在承诺中时,我如何使用承诺来捕获错误?
【发布时间】:2017-08-06 02:39:11
【问题描述】:

背景

我正在使用 Promise,并且我有许多函数可能会或可能不会返回 Promise,并且可能会或可能不会失败,如下例所示:

//does not return a Promise, simply a string
let goodFun = function(){
    return "I like bananas!";
};

//blows up!
let badFun = function(){
    throw "A general error blaahh!"; 
};

//You get the point ...

由于这些函数可能会或可能不会返回 Promises,并且可能会或可能不会失败,因此我需要等待它们全部执行。为了实现这一点,我有一个函数可以调用它们并等待它们执行:

let asyncFun = function(){
    return Promise.all([badFun(), goodFun()]);
};

问题

到目前为止一切顺利。我的代码调用asyncFun,我希望它的某些功能实际上会失败。为了做好准备,我添加了一个问题:

let executor = function(){

    let numbsArray = [1, 2, 3];
    let respArray = [];

    for(let num of numbsArray){
        respArray.push(
            asyncFun()
                .catch( error => console.log(`I failed with ${error} and ${num}`))
        );
    }

    return Promise.all(respArray);
};

问题是catch 根本没有捕捉到任何东西!

即使在调用executor 的函数中添加一个catch 也没有捕捉到任何东西!

executor()
    .catch(error => console.log("Failed miserably to catch error!"));

研究

我真的不明白为什么我的 catch 子句没有捕捉到异常。为了找出答案,我阅读了这个讨论:

这让我相信我的所有函数 goodFunbadFun 无论如何都必须返回一个承诺。

这让我很困惑,因为根据MDN documentation,数组可能包含一个 Promise,或者一个结果(如字符串或数字)。

我还想避免在我的函数中添加 更多 样板代码 ....

问题:

  1. 如何修复我的代码,以便添加最低限度或样板代码的捕获器起作用?

代码

let goodFun = function() {
  return "I like bananas!";
};

let badFun = function() {
  throw "A general error blaahh!";
};



let asyncFun = function() {
  return Promise.all([badFun(), goodFun()]);
};

let executor = function() {

  let numbsArray = [1, 2, 3];
  let respArray = [];

  for (let num of numbsArray) {
    respArray.push(
      asyncFun()
      .catch(error => console.log(`I failed with ${error} and ${num}`))
    );
  }

  return Promise.all(respArray);
};

executor()
  .catch(error => console.log("Failed miserably to catch error!"));

【问题讨论】:

    标签: javascript node.js promise es6-promise


    【解决方案1】:

    您的.catches 不起作用的原因是错误被同步地抛出。代码执行甚至从不执行 .catch() 指令来设置 catch 处理程序,因为错误已经被抛出并且代码执行已经转移到其他地方。如果您将所有内容都包装在一个普通的 try-catch 中,我想您会看到它捕捉到您抛出的错误。

    例子:

    let goodFun = function() {
      return "I like bananas!";
    };
    
    //blows up!
    let badFun = function() {
      throw "A general error blaahh!";
    };
    
    try {
      Promise.all([goodFun(), badFun()])
        .then(results => console.log(results))
        .catch(error => console.error(error))
    } catch (e) {
      console.error("Wups, I caught an error. This wasn't supposed to happen.", e);
    }

    这让我很困惑,因为根据 MDN 文档,数组可能包含一个 Promise,或者来自一个 Promise 的结果(如字符串或数字)。

    MDN 文档是绝对正确的,但是如果 badFun() 抛出错误,Promise.all 将无能为力,因为程序流将沿着调用堆栈向下寻找一些东西来捕获错误被扔了。

    如果您不确定某些代码是同步的还是基于 Promise 的(或者它是否会引发同步错误),您可以使用它来安全地将其包装成 Promise:

    function executeSafe(action) {
        return Promise.resolve().then(action);
    }
    

    这将保护 action() 免受任何发生的“繁荣”。

    例子:

    let executeSafe = 
      action => Promise.resolve().then(action);
    
    let goodFun = 
      () => "I like bananas!";
    
    //blows up!
    let badFun = 
      () => { throw "A general error blaahh!" };
    
    Promise.all([goodFun, badFun].map(executeSafe))
      .then(results => console.log(results))
      .catch(error => console.error(error))

    仅供参考,如果您使用的是 Bluebird,那么它有一个内置方法 Promise.try,其用途与上述 executeSafe 相同:Promise.all([goodFun, badFun].map(Promise.try))

    这是您需要修改的完整示例:

    let executeSafe =
      action => Promise.resolve().then(action);
    
    let goodFun =
      () => "I like bananas!";
    
    //blows up!
    let badFun =
      () => {
        throw "A general error blaahh!"
      };
    
    let asyncFun =
      () => Promise.all([goodFun, badFun].map(executeSafe));
    
    let executor = function() {
      let numbsArray = [1, 2, 3];
    
      return Promise.all(numbsArray.map(num =>
        asyncFun()
        .catch(error => console.log(`I failed with ${error} and ${num}`))
      ));
    }
    
    executor();

    这里是.catchexecutor 之外:

    let executeSafe =
      action => Promise.resolve().then(action);
    
    let goodFun =
      () => "I like bananas!";
    
    //blows up!
    let badFun =
      () => {
        throw "A general error blaahh!"
      };
    
    let asyncFun =
      () => Promise.all([goodFun, badFun].map(executeSafe));
    
    let executor = function() {
      let numbsArray = [1, 2, 3];
    
      return Promise.all(numbsArray.map(asyncFun));
    }
    
    executor().catch(error => console.error("Miserably failed to catch anything.", error));

    【讨论】:

    • 按照function executeSafe(action) { return new Promise(function(resolve, reject){ try { return resolve(action()); } catch(e) { reject(e); } 的方式做一些事情不是更好吗?
    • @GGG 不,那将是非常多余的。 .then() 内部的逻辑旨在捕获错误并将其转化为被拒绝的 Promise。
    • 哦,好吧,我不知道 .then() 内部的这种行为
    • @GGG 是的。还要记住,new Promise() 也会将错误转化为被拒绝的承诺,所以像这样在里面放一个 try-catch 是没有用的。
    • 谢谢!我将重新阅读文档以更新自己的 Promises
    猜你喜欢
    • 2022-12-19
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    • 2021-03-03
    • 2018-05-12
    • 2019-08-20
    • 2022-09-24
    • 2023-01-30
    相关资源
    最近更新 更多