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

How do I properly write a module to be compatible with Bluebird's `PromisifyAll`

假设在 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
};

我在 main.js 中消耗了 moduleA.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 undefinedPromise.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.delaythis 未定义。

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

提前致谢。

promisifyAll 创建依赖于 this 的方法,因为当您将 class 原型传递给它将创建多个对象时,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" 被 promified 时没有任何绑定,这些方法依赖于特定的 client 实例。 .putAsync 不能只调用 put - 它需要在 client1client2 的上下文中调用 put,具体取决于 putAsync 的调用方式。

有人向遇到同样问题的人提出了解决方案,但他从未回应:[​​=23=]。


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

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

您在 moduleA.js 中的问题较少,在 main.js 中的使用问题较多。

如果您在 .then() 语句中显式调用 promisified 函数,您的模块将正常工作:

// 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 没有做的上下文绑定。这可以解释为什么将您的方法包装在一个干净的函数中可以解决这个问题。

我相信 Bluebird 的维护者会知道更多,如果您想获得有关实现的细节。