Q promises传播错误和异常

时间:2014-05-01 14:57:45

标签: javascript node.js error-handling q

我正在使用此代码将经典的nodejs函数转换为promise 1:

  Object.defineProperty(Function.prototype, "toPromise", {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function(self) {
      var $this;
      $this = this;
      return function() {
        var arg, args, deferred, _i, _len;
        deferred = Q.defer();
        args = [];
        for (_i = 0, _len = arguments.length; _i < _len; _i++) {
          arg = arguments[_i];
          args.push(arg);
        }
        args.push(function() {
          args = Array.prototype.slice.call(arguments);
          if (args[0] instanceof Error) {
            return deferred.reject.apply($this, args);
          } else {
            return deferred.resolve.apply($this, args);
          }
        });
        $this.apply(self, args);
        return deferred.promise;
      };
    }
  });

我在函数上调用它来获取另一个函数。像那样:

  exports.list = (function(userid, options, callback) {
    // do something 
    // success ->
        callback(data);
    // error ->
        callback(err)
  }).toPromise(this);

但是当抛出一个未被我抛出的异常(SyntaxError,TypeError ...)时,不会调用thenfail函数。如何自动传播?

我试图用那个替换toPromise但是这没用(即使它正在使用我的函数)

  Object.defineProperty(Function.prototype, "toPromise", {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function(self) {
      var $this;
      $this = this;
      return Q.denodeify(this.bind(Kitty));
    }
  });

我还尝试对then应用第二次回调,但我也没有用。

1 个答案:

答案 0 :(得分:2)

  

但是当抛出一个未被我抛出的异常(SyntaxError,TypeError ...)时,不会调用then或fail函数。

Q仅捕获从.then()回调引发的异常。您需要明确地处理所有其他异常。

  

如何自动传播?

如果从您自己的代码中抛出异常,您可以考虑在较低级别使用Promise,并将您自己的代码仅放在then回调中。

如果从您调用的“经典nodejs函数”抛出异常,则需要catch它。但是,可能有很好的理由抛出异常(这是相当不可恢复的),而不是仅使用error参数调用回调,这将是异步节点函数的“正常”设计方法。

如果您希望在toPromise方法中包含此类功能,则需要包装函数调用:

try {
  $this.apply(self, args);
} catch(e) {
  deferred.resolve(e);
}

(function(…, callback) { … }).toPromise(this);

这是一个坏主意。您不应该在自己的函数上使用toPromise,而只应使用赋予您并具有非promise接口的函数。{1}}。请参阅“教程”部分中的The Beginning,了解如何提供真正返回Promise的函数。不要使用callback变量。很容易让一些错误逃脱。特别是如果你有一个已经产生承诺的函数,你需要做的就是在它之后链接其他任务。

在您的具体情况下,它只是

exports.list = function(userid, options) {
  return canThis(userid, "mod", "browse").then(function(can) {
    if (can === false)
      throw error.throwError("Forbidden", "UNAUTHORIZED");
    if (options.perPage > 50) {
      throw error.throwError("Too much mods per page", "INVALID_PARAMS");

    var Mod = mongoose.model("Mod");
    return Q.all([ // I would assume that listing and counting can happen in parallel?
      Q.ninvoke(Mod, "list", options),
      Q.ninvoke(Mod.count(), "exec")
    ]).spread(function(mods, count) {
      mods.totalCount = count;
      return mods;
    }, function(err) { // and throw this error when one happens in either?
      throw error.throwError(err, "DATABASE_ERROR");
      // not sure whether you need errors.handleResult at all
      // (not with a callback, at least)
    });
  });
};

现在所有这些return实际上都有意义。