【问题标题】:Stop error propagation in Bluebird promises在 Bluebird Promise 中停止错误传播
【发布时间】:2015-03-01 18:10:14
【问题描述】:

如何阻止抛出的错误在整个链条中传播?它显示在我的 catch() 块中,但它不会停止并导致服务器崩溃并出现未捕获的异常。

我将其作为节点 cron 作业 (node-cron) 的一部分运行:

var cronJob = require('cron').CronJob;
var cron = require('../lib/cron')

var c = new cronJob('* * * * * *', function() {
  console.log('Cron starting');
  mycode.run();
}, function() {
  console.log('Cron executed');
}, true);
 c.start();

在我的 cron.js 中

  module.exports = {
    run: function() {
      return job.getAndStore().catch(function(e) {
        // This prints but it keeps on going so to speak - it doesn't 'catch', just notifies me
        console.log('ERROR', e); 
      });
    }
  };

控制台转储:

Cron starting
ERROR [TypeError: undefined is not a function]
Cron starting
Uncaught Exception
[TypeError: undefined is not a function]
TypeError: undefined is not a function

我必须这样做,但我知道这不太对:

try {
  run();
} catch(e) { 
  console.log('Now it stops')
}

run() 是某些不支持任何承诺的 cron 库的一部分,因此我将其包装在函数中以调用它。

编辑我认为我的问题与后续调用有关,我认为这与我在 2 次以上调用中处理 Mongo 连接的方式有关:

    //  Create a Mongo connection
Job.prototype.getDb = function(id) {
  var self = this;
  return new P(function(resolve, reject) {
    if (!self.db) {
      return Mongo.connectAsync(self.options.connection)
      .then(function(c) {
        self.db = c;
        debug('Got new connection');
        resolve(c);
      });
    }
    debug('Got existing connection');
    resolve(self.db);
  });
};

// Fetch stuff
Job.prototype.getAndStore = function(c) {
  return this.getDb().then(function() {
    throw new Error('Boom');
  });
};

【问题讨论】:

  • 它不会停止并导致服务器崩溃”是什么意思?错误究竟是在哪里引发的?如果你的 catch 回调被调用,你应该是安全的。
  • 您的代码中还有另一个问题 - 向我们展示堆栈跟踪。
  • 添加了更多代码 - 它在 promise 的 catch 块中打印出我的错误,然后“继续”,可以这么说,导致整个应用程序崩溃。我不确定我的错误在哪里

标签: javascript promise bluebird


【解决方案1】:

您的catch 回调仅在第一次执行。您在 cron 作业的第二次运行中遇到了未捕获的异常,看起来您的 job.getAndStore() 不会在那里返回被拒绝的承诺,而是同步地返回 throws。不应该,它should always return a promise

您可以使用Bluebirds Promise.try 自动捕获此类异常并将其转换为 Promise 拒绝。或者你将 getAndStore 函数包装在 Promise.method 中:

var safeGetAndStore = Promise.method(job.getAndStore.bind(job));

module.exports = {
  run: function() {
    return safeGetAndStore().catch(function(e) {
      console.log('ERROR', e); 
    });
  }
};

在您的具体情况下,问题在于您的 job 确实缓存了 db 连接并在它已经可用时返回了该连接 - 但您需要使用 .then 方法返回一个承诺。您应该简单地缓存承诺本身:

Job.prototype.getDb = function(id) {
  if (!this.db) {
    this.db = Mongo.connectAsync(self.options.connection);
  return this.db;
};

【讨论】:

  • 好的 - 安全的方法可以解决问题。如果我不这样做,那么只有第一个 cron 调用会被捕获,而任何后续调用都不会。我没有意识到这个 cron 模块下降到几秒钟,所以堆栈跟踪是 2 次正常后续调用的结果。我想知道为什么只有第一个电话被抓住了
  • 在第一次调用中由于某种原因它没有抛出异常,而是返回了一个被拒绝的承诺。如果您向我展示了您的 getAndStore 函数,我可以告诉您更多相关信息 :-)
  • 我发现了我的问题 - 我正在创建一个 mongo 连接并保存它,但这不是后续调用的承诺。我已经在 P(resolve, reject) 调用中包装了这个东西,尽管也许有更好的方法来处理这种有时是异步的情况
  • 是的,但不幸的是,您使用an antipattern 解决了这个问题。看看我的答案更新
【解决方案2】:

使用done,至少如果 bluebird 正确实现它,它会按您的预期工作。

catch(..) 只是 then(null, ..) 的别名,它是一个承诺转换器,它创建另一个承诺以供进一步处理。

所以以下应该适合你:

  module.exports = {
    run: function() {
      return job.getAndStore().done(null, function(e) {
        // This prints but it keeps on going so to speak - it doesn't 'catch', just notifies me
        console.log('ERROR', e); 
      });
    }
  };

【讨论】:

    猜你喜欢
    • 2015-08-28
    • 1970-01-01
    • 2018-07-06
    • 1970-01-01
    • 2018-11-02
    • 2018-06-25
    • 2016-07-12
    • 2016-05-14
    • 1970-01-01
    相关资源
    最近更新 更多