Search code examples
node.jsecmascript-6promisebluebird

Omitting arguments causes promisified functions to fail for ES2015 classes


I'm having trouble getting the Bluebird library's promisifyAll function to work with an ES2015 class's object instance methods when arguments are omitted. I'm using Node 7.8.0. Here's some sample code:

// dog.js
class Dog {
  constructor(opts) {
    this.name = opts.name || 'Fido';
  }

  fetchBone(bone, callback) {
    console.log('fetching the bone');
    callback(null, 'got it');
  }
}

exports = module.exports = Dog;

Let's say the bone is an optional argument for fetchBone. When I pass it, everything's fine.

> var Dog = require('./dog');
> Promise = require('bluebird');
> Promise.promisifyAll(Dog); // omitting this line doesn't help
> Promise.promisifyAll(Dog.prototype);
> var puppy = new Dog();
> puppy.fetchBoneAsync('juicy bone')
       .then(result => console.log('from promise:', result))
       .catch(err => console.error('failure:', err));
fetching the bone
Promise {
  _bitField: 0,
  _fulfillmentHandler0: undefined,
  _rejectionHandler0: undefined,
  _promise0: undefined,
  _receiver0: undefined }
> from promise: got it

It fails when I don't pass in the bone.

> puppy.fetchBoneAsync()
       .then(result => console.log('from promise:', result))
       .catch(err => console.error('failure:', err));
fetching the bone
Promise {
  _bitField: 0,
  _fulfillmentHandler0: undefined,
  _rejectionHandler0: undefined,
  _promise0: undefined,
  _receiver0: undefined }
> failure: TypeError: callback is not a function
    at Dog.fetchBone (dog.js:8:5)
    at Dog.tryCatcher (node_modules/bluebird/js/release/util.js:16:23)
    at Dog.ret [as fetchBoneAsync] (eval at makeNodePromisifiedEval (node_modules/bluebird/js/release/promisify.js:184:12), <anonymous>:14:23)
    at repl:1:7
    at ContextifyScript.Script.runInThisContext (vm.js:23:33)
    at REPLServer.defaultEval (repl.js:339:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.onLine (repl.js:536:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:191:7)
    at REPLServer.Interface._onLine (readline.js:241:10)
    at REPLServer.Interface._line (readline.js:590:8)
    at REPLServer.Interface._ttyWrite (readline.js:869:14)
    at REPLServer.self._ttyWrite (repl.js:609:7)
    at ReadStream.onkeypress (readline.js:120:10)

Strangely, it works if I pass in undefined for the bone.

> puppy.fetchBoneAsync(undefined)
       .then(result => console.log('from promise:', result))
       .catch(err => console.error('failure:', err));
fetching the bone
Promise {
  _bitField: 0,
  _fulfillmentHandler0: undefined,
  _rejectionHandler0: undefined,
  _promise0: undefined,
  _receiver0: undefined }
> from promise: got it

Anybody know what's going on here?


Solution

  • Yep, that is correct behavior. The promises are mapped to the original functions. So fetchBone() is receiving two arguments in the first call and one in the second example.

    This means that in the second example, when fetchBone() is called callback is undefined and the bone itself is the callback function created by the promisification to handle the promise itself.