Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand how promisification works with BlueBird

I'm trying to wrap my head around promises using the Bluebird library for Node.js.

Below is a simple example that doesn't work as I would expect.

var Promise = require("bluebird");

var myObj = {
  add: function(op1, op2) {
    return op1 + op2;
  }
};

// Sync call to add method -> 7
console.log(myObj.add(3,4));

var myObjAsync = Promise.promisifyAll(myObj);

// Async call to promisified add method -> nothing written to console
myObjAsync.addAsync(2,3).then(function(data) {
  console.log(data);
  return data;
})

I'm either missing some (major) concept with promises or with Bluebird.

Thanks in advance for your help.

EDIT: Revised (and now working version) based on feedback from jfriend00.

var Promise = require("bluebird");

var myObj = {
  add: function(op1, op2) {
    return op1 + op2;
  }
  , add2: function(op1, op2, callback) {
    callback(null, op1 + op2);
  }

};

// Sync call to add method -> 7
console.log(myObj.add(3,4));

var myObjAsync = Promise.promisifyAll(myObj);

// Async call to promisified add method -> nothing written to console
myObjAsync.addAsync(2,3).then(function(data) {
  console.log("%j", data);
  return data;
})

// Async call to promisified add2 method -> 5
myObjAsync.add2Async(2,3).then(function(data) {
  console.log("%j", data);
  return data;
})
like image 264
James Sun Avatar asked Oct 15 '14 20:10

James Sun


People also ask

How does promisify work?

promisify() method basically takes a function as an input that follows the common Node. js callback style, i.e., with a (err, value) and returns a version of the same that returns a promise instead of a callback.

What is promisification?

“Promisification” is a long word for a simple transformation. It's the conversion of a function that accepts a callback into a function that returns a promise. Such transformations are often required in real-life, as many functions and libraries are callback-based.

What is Bluebird promise?

What is Bluebird JS? Bluebird JS is a fully-featured Promise library for JavaScript. The strongest feature of Bluebird is that it allows you to “promisify” other Node modules in order to use them asynchronously. Promisify is a concept applied to callback functions.

What does promise Promisify do?

promisify. Returns a function that will wrap the given nodeFunction . Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function.


1 Answers

For promisifyAll() to work, the function has to be asynchronous and the last argument passed to the function has to be the completion callback and the completion callback has to have as its first argument, an error argument that is falsey when there's no error and the return value as the second argument (if there is a value).

Your function doesn't meet any of those criteria.


Here's an excerpt from the Bluebird doc for .promisifyAll():

The target methods are assumed to conform to node.js callback convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument. If the node method calls its callback with multiple success values, the fulfillment value will be an array of them.

Please remember that .promisifyAll() can't make a synchronous operation into an async operation. What is does is take an asynchronous operation that conforms to a specific calling convention and wraps that into in a promise by hooking the callback and testing the callback's arguments to detect success or failure and to propagate the return value.


If you're curious about how Bluebird does this, you can inspect their actual code here on Github, though it is not super easy to follow exactly what it does without some significant study.

Here's a bit simpler version of a promisify function to just see what it does (I'd recommend using Bluebird for all of it's other features rather than this).

// --------------------------------------------------------------
// promisify(fn, obj)
//
// Pass an async function that takes as its last argument a callback
// that conforms to the node.js callback calling convention function(err, result)
// passing obj is optional.  If present the function passed in will be called
// as obj.method()
//
// Returns: New function that when called will return a promise.
// --------------------------------------------------------------

function promisify(fn, obj) {
    if (typeof fn !== "function") {
        throw new Error("fn argument to promisify() must be function");
    }
    // obj is optional and may be undefined
    // if present, it will be used as context to call fn as in obj.fn()
    return function(/* args */) {
        // make copy of arguments object into a real array in a way that
        // does not prevent interpreter optimizations
        var args = new Array(arguments.length);
        for (var i = 0; i < args.length; i++) {
            args[i] = arguments[i];
        }
        return new Promise(function(resolve, reject) {
            // add our callback function at the end of the args list
            var resultMany;
            args.push(function(err, result) {
                if (err) {
                    reject(err);
                } else {
                    // if 0 or 1 result, then just return it as a simple value
                    if (arguments.length <= 2) {
                        resolve(result);
                    } else {
                        // if more than one result came with the callback function, 
                        // then put it into an array so we can resolve with a single value (the array of results)
                        // skip the first argument which is the err value
                        resultMany = new Array(arguments.length - 1);
                        for (var i = 0; i < arguments.length - 1; i++) {
                            resultMany[i] = arguments[i + 1];
                        }
                        resolve(resultMany);
                    }
                }
            });
            // call original function with our callback as last argument
            fn.apply(obj, args);
        });
    }
}

Here's a working demo of this promisify() function: https://jsfiddle.net/jfriend00/m1265vos/

like image 55
jfriend00 Avatar answered Oct 13 '22 23:10

jfriend00