使用bluebird promise进行异步异常处理

Asynchronous exception handling with bluebird promises

本文关键字:异步 异常处理 bluebird promise 使用      更新时间:2023-09-26

处理这种情况的最佳方法是什么。我在一个可控的环境中,我不想撞车。

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之前,那么蓝鸟接球会接住它:

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!]

这很好,但如何处理节点或浏览器中这种性质的恶意异步回调。

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

然而,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(以获得期望的行为。永远不要抛出自定义(非promise(异步回调,永远拒绝周围的promise。如果确实需要,请使用try-catch

在处理了您所描述的相同场景和需求后,我发现了zone.js,这是一个在多个框架中使用的令人惊叹的javascript库(Angular就是其中之一(,它使我们能够以非常优雅的方式处理这些场景。

Zone是一个在异步任务中持续存在的执行上下文。您可以将其视为JavaScript虚拟机的线程本地存储

使用示例代码:

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

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

注意:拒绝调用后,函数将继续运行。

示例1:拒绝,然后在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);
  });