Question

I am using this code in order to convert a classic nodejs function into a promise one:

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

And I call it on a function to get another function. Like that:

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

But when an exception that wasn't thrown by me (SyntaxError, TypeError...) is thrown, the then or fail function isn't called. How to propagate it automatically?

I tried to replace toPromise by that but this didn't worked (even when it was working with my function)

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

I also tried applying a second callback to then but i didn't worked either.

Was it helpful?

Solution

But when an exception that wasn't thrown by me (SyntaxError, TypeError...) is thrown, the then or fail function isn't called.

Q does only catch the exceptions that are thrown from a .then() callback. You need to deal with all other exceptions yourself explicitly.

How to propagate it automatically?

If the exception is thrown from your own code, you might consider using Promises on a lower level, and put your own code in then callbacks only.

If the exception is thrown from "classic nodejs function" that you call, you will need to catch it. However, there might be good reasons why an exception is thrown (which is rather unrecoverable) in instead of just calling the callback with the error argument, which would be the "normal" design approach of an asynchronous node function.

If you would want to include such feature in your toPromise method, it would require to wrap the function call:

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

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

That's a bad idea. You should not use toPromise on your own functions, but only those that are given to you and have the non-promise interface. See The Beginning in the Tutorial section how to come up with a function that returns a Promise genuinely. Do not use a callback variable. Too easily some error escapes you. Especially if you have a function that already yields a promise, all you need to do is chain other task after it.

In your specific case, it's simply

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

Now all those returns actually make sense.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top