【问题标题】:Asynchronous exception handling with bluebird promises使用 bluebird Promise 处理异步异常
【发布时间】:2014-09-28 09:22:15
【问题描述】:

处理这种情况的最佳方法是什么。我处于受控环境中,我不想崩溃。

var Promise = require('bluebird');

function getPromise(){
    return new Promise(function(done, reject){
        setTimeout(function(){
                throw new Error("AJAJAJA");
        }, 500);
    });
}

var p = getPromise();
    p.then(function(){
        console.log("Yay");
    }).error(function(e){
        console.log("Rejected",e);
    }).catch(Error, function(e){
        console.log("Error",e);
    }).catch(function(e){
        console.log("Unknown", e);
    });

当从 setTimeout 内抛出时,我们总是会得到:

$ node bluebird.js

c:\blp\rplus\bbcode\scratchboard\bluebird.js:6
                throw new Error("AJAJAJA");
                      ^
Error: AJAJAJA
    at null._onTimeout (c:\blp\rplus\bbcode\scratchboard\bluebird.js:6:23)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

如果抛出发生在 setTimeout 之前,那么 bluebirds catch 会捡起它:

var Promise = require('bluebird');

function getPromise(){

    return new Promise(function(done, reject){
        throw new Error("Oh no!");
        setTimeout(function(){
            console.log("hihihihi")
        }, 500);
    });
}

var p = getPromise();
    p.then(function(){
        console.log("Yay");
    }).error(function(e){
        console.log("Rejected",e);
    }).catch(Error, function(e){
        console.log("Error",e);
    }).catch(function(e){
        console.log("Unknown", e);
    });

结果:

$ node bluebird.js
Error [Error: Oh no!]

这很好 - 但是如何在节点或浏览器中处理这种性质的流氓异步回调。

【问题讨论】:

标签: javascript node.js asynchronous promise bluebird


【解决方案1】:

在处理了您描述的相同场景和需求后,我发现了zone.js,这是一个了不起的 javascript 库,用于多个框架(Angular 就是其中之一),它允许我们以非常简单的方式处理这些场景优雅的方式。

区域是跨异步任务持续存在的执行上下文。您可以将其视为 JavaScript VM 的线程本地存储

使用您的示例代码:

import 'zone.js'

function getPromise(){
  return new Promise(function(done, reject){
    setTimeout(function(){
      throw new Error("AJAJAJA");
    }, 500);
  });
}

Zone.current
  .fork({
    name: 'your-zone-name',
    onHandleError: function(parent, current, target, error) {
      // handle the error 
      console.log(error.message) // --> 'AJAJAJA'
      // and return false to prevent it to be re-thrown
      return false
    }
  })
  .runGuarded(async () => {
    await getPromise()
  })

【讨论】:

    【解决方案2】:

    感谢@Bergi。现在我知道 promise 不会在异步回调中捕获错误。这是我测试过的 3 个示例。

    注意:来电拒接后,函数会继续运行。

    示例1:reject,然后在promise构造函数回调中抛出错误

    示例 2:拒绝,然后在 setTimeout 异步回调中抛出错误

    示例 3:拒绝,然后在 setTimeout 异步回调中返回以避免崩溃

    // Caught
    // only error 1 is sent
    // error 2 is reached but not send reject again
    new Promise((resolve, reject) => {
      reject("error 1"); // Send reject
      console.log("Continue"); // Print 
      throw new Error("error 2"); // Nothing happen
    })
      .then(() => {})
      .catch(err => {
        console.log("Error", err);
      });
    
    
    // Uncaught
    // error due to throw new Error() in setTimeout async callback
    // solution: return after reject
    new Promise((resolve, reject) => {
      setTimeout(() => {
        reject("error 1"); // Send reject
        console.log("Continue"); // Print
    
        throw new Error("error 2"); // Did run and cause Uncaught error
      }, 0);
    })
      .then(data => {})
      .catch(err => {
        console.log("Error", err);
      });
    
    
    // Caught
    // Only error 1 is sent
    // error 2 cannot be reached but can cause potential uncaught error if err = null
    new Promise((resolve, reject) => {
      setTimeout(() => {
        const err = "error 1";
        if (err) {
          reject(err); // Send reject
          console.log("Continue"); // Did print
          return;
        }
        throw new Error("error 2"); // Potential Uncaught error if err = null
      }, 0);
    })
      .then(data => {})
      .catch(err => {
        console.log("Error", err);
      });
    

    【讨论】:

      【解决方案3】:

      Promise 不是domains,它们不会从异步回调中捕获异常。你就是不能那样做。

      然而,Promise 会捕获从 then / catch / Promise 构造函数回调中抛出的异常。所以使用

      function getPromise(){
          return new Promise(function(done, reject){
              setTimeout(done, 500);
          }).then(function() {
              console.log("hihihihi");
              throw new Error("Oh no!");
          });
      }
      

      (或只是Promise.delay)以获得所需的行为。永远不要抛出自定义(非承诺)异步回调,总是拒绝周围的承诺。如果确实需要,请使用try-catch

      【讨论】:

      • 如果我无法控制 setTimeout 调用的方法中发生的事情怎么办?根据@Kevin B 的建议,再写一个承诺?
      • 没有。当它失控时,你就无法捕捉到错误。针对您需要获得控制权的库提交错误。
      • 嗯,检查 mocha 在幕后做了什么,似乎处理类似:it("应该用 hello world 响应", function(done) { setTimeout(function(){ throw new Error (“哎哟”); 完成(); }, 500); });非常优雅。但可能不是承诺。
      • 在浏览器中可能使用window.onerror,在节点中可能使用域。
      • 在亚军中:process.on('uncaughtException', uncaught);
      猜你喜欢
      • 2018-10-15
      • 1970-01-01
      • 1970-01-01
      • 2011-09-11
      • 2019-08-29
      • 2012-12-27
      • 1970-01-01
      相关资源
      最近更新 更多