如何正确编写模块以与Bluebird的“PromisifyAll”兼容

时间:2015-02-03 15:38:44

标签: javascript node.js promise bluebird

让我们在node.js模块moduleA.js中说,我有以下一些节点式异步函数的对象:

// moduleA.js

var init = function (data, callback) {
    return callback(null, data.params );
};

var delay = function(data, callback) {
    setTimeout(function(){
        callback(null, data);
    }, 1000*3);
}


var reverse = function(data, callback) {
    var j,
        d = {};

    for(j in data) {
        d[ data[j] ] = j;
    }

    callback(null, d);

}

module.exports = {
    init: init,
    delay: delay,
    reverse: reverse
};

我在moduleA.js中消费main.js并且可以单独成功promisify每个方法,例如:

// main.js
var Promise = require('bluebird'),
    modA = require('./moduleA') );

var data = {
    "a": "one",
    "b": "two",
    "c": "three",
    "d": "four"
};


Promise.promisify(modA.init)(data)

    .then( Promise.promisify(modA.delay) )

    .then( Promise.promisify(modA.reverse) )

    .then(function(data){

        res.send(data);

    }).catch(function(e){
        next(e);
    });

上面的代码工作正常,但比想要的更详细。

我的问题是,如何修改模块以允许Promise.promisifyAll在整个导出的对象上正常工作?我想避免在模块中使用promisification,并允许其他人在消费时选择promisify

我未能成功尝试以下几种变体:

// main.js
var Promise = require('bluebird'),
    modA = require('./moduleA') ),
    modAPromised = Promise.promisifyAll(modA);

var data = {
    "a": "one",
    "b": "two",
    "c": "three",
    "d": "four"
};

modAPromised.initAsync(data)

    .then(modAPromised.delayAsync)

    .then(modAPromised.reverseAsync)

    .then(function(data){

        res.send(data);

    }).catch(function(e){
        next(e);
    });

当我这样做时,我得到了Cannot call method 'delay' of undefined的错误。 Promise.promisifyAll正在按预期添加所有Async个功能:

// console.log(modAPromised);

{
    init: [Function],
    delay: [Function],
    reverse: [Function],
    initAsync: {
        [Function] __isPromisified__: true
    },
    delayAsync: {
        [Function] __isPromisified__: true
    },
    reverseAsync: {
        [Function] __isPromisified__: true
    }
}

但我觉得有些事情与背景有关。看来内部delayAsync尝试调用this.delay,但this未定义。

那么,我如何修改我的模块以允许Promise.promisifyAll在整个导出的对象上正常工作?

提前致谢。

2 个答案:

答案 0 :(得分:1)

promisifyAll创建依赖于this的方法,因为当您将类原型传递给它时,this是未知的,将从中创建多个对象。

例如:

Promise.promisifyAll(require("redis"));

// In another file
var redis = require("redis");
var client1 = redis.createClient(...);
var client2 = redis.createClient(...);

client1.putAsync(...);
client2.putAsync(...);

"redis"被宣传时,没有任何东西可以绑定,并且这些方法依赖于特定的client个实例。 .putAsync无法拨打put - 它需要在putclient1的上下文中调用client2,具体取决于putAsync的调用方式。< / p>

有人向有同样问题的人提出解决方案,但他从未做出回应:https://github.com/petkaantonov/bluebird/issues/470


您的模块可以使用nodeify实现为双API,这样消费者甚至不需要宣传:

var init = function (data, callback) {
    return Promise.resolve(data.params).nodeify(callback);
};

var delay = function(data, callback) {
    return Promise.delay(data, 1000 * 3).nodeify(callback);
}

var reverse = function(data, callback) {
    var j,
        d = {};

    for(j in data) {
        d[ data[j] ] = j;
    }

    return Promise.resolve(d).nodeify(callback);
}

答案 1 :(得分:0)

moduleA.js使用main.js以及.then()中使用情况的问题更少。

如果您在// main.js var Promise = require('bluebird'), modA = require('./moduleA'), modAPromised = Promise.promisifyAll(modA); var data = { "a": "one", "b": "two", "c": "three", "d": "four" }; modAPromised.initAsync(data) .then(function(data) { return modAPromised.delayAsync(data); }) .then(function(data) { return modAPromised.reverseAsync(data); }) .then(function(data){ res.send(data); }).catch(function(e){ next(e); }); 语句中明确调用了已宣传的函数,那么您的模块将正常运行:

promisifyAll

至于为什么这是,我老实说不确定。我强烈怀疑promise做了一些{{1}}没有做的上下文绑定。这可以解释为什么将方法包装在一个干净的函数中可以解决这个问题。

如果你想了解实施的具体细节,我确定蓝鸟维护者会知道更多。