Trying to understand how promisification works wit

2020-02-03 09:09发布

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

1条回答
家丑人穷心不美
2楼-- · 2020-02-03 09:32

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/

查看更多
登录 后发表回答