node.js redis以及在使用模块时如何使用promise

node.js redis and how to use promise when using a module

我在节点服务器中有这样的 Express 路由(需要文件):

var redis = require('../modules/redis');

module.exports = function (app) {

var redisClient = redis.init();


app.post('/auth/ticket', cors(), function (req, res) {


    var hashes = ['hash1','hash2', 'hash3'];

    var candidates = [];  // An array to collect valid hashes
    var key;  
    // to check each hash against a RedisDB I use a For Loop
    for (key in hashes) {
        var hash = hashes[key];
        console.log("Hash " + hash + "  will be proofed now:");
       //now I try to collect the valid hashes in the candidates array
       if (redisClient.exists(hash) === 1) candidates.push(hash);
    }
    console.log(JSON.stringify(candidates));
});
};

现在这是我的模块的代码,它将管理所有的 redis 请求:

exports.init = function () {
Redis = exports.Redis = function () {
    var promiseFactory = require("q").Promise,
        redis = require('promise-redis')(promiseFactory);

    this.client = redis.createClient();
    this.client.on('error', function (err) {
        console.log('redis error – ' + client.host + ':' + client.port + ' – ' + err);
    });

Redis.prototype.exists = function (key) {
    this.client.exists(key, function (err, data) {
       return data === 1 ? true : false;
    });
};

return new Redis();
};

所以我的经验是该模块能够正确 console.log 结果。如果哈希有效,则 returns 为真,否则为假。这按预期工作。 问题是,for 循环继续执行而不获取结果。我认为这是由竞争条件引起的。

如您所见,我已经开始在我的代码顶部使用 Q 和 promise-redis 进行一些锻炼:

 var promiseFactory = require("q").Promise,
    redis = require('promise-redis')(promiseFactory);

this.client = redis.createClient();

我想知道,我如何让我的 for 循环(在 Express 路线中)等待 redisClient.exists(hash) 的结果,或者换句话说,将所有有效的哈希值放入我的候选数组中.

请帮忙

就像@brad 说的,你可以使用Q.all,它会接受一个承诺数组作为输入,然后return所有承诺完成后的结果数组:

您的回答有误:

Redis.prototype.exists = function (key) {

return this.client.exists(key)      // CHANGED, you still need to return a promise.
    .then(function (reply) {
        console.log("reply " + reply);
        return (reply);
    })
    .catch(console.log);

};

如果我没理解错的话,你想要的是这样的

exports.init = function () {
Redis = exports.Redis = function () {
    var Q = require("q"),
        promiseFactory = Q.Promise,
        redis = require('promise-redis')(promiseFactory);

    this.client = redis.createClient();
    this.client.on('error', function (err) {
        console.log('redis error – ' + client.host + ':' + client.port + ' – ' + err);
    });

Redis.prototype.exists = function (key) {
    return this.client.exists(key).then(function (data) {
       return data === 1 ? true : false;
    });
};

Redis.prototype.getActive = function (arry) {
    var self = this;
    return  Q.all(arry.map(self.exists.bind(self))
            ).then(function(res){
                return arry.filter(function(val, idx){ return res[idx];});
            });
};



return new Redis();
};

@mido22:但是你有没有发现我把所有的reds函数都外包给了需要promise-redid的模块文件(1st Codeblock),并为Q建立了一个工厂。我把模块文件里面的代码改成了:

    Redis.prototype.exists = function (key) {

    this.client.exists(key)
        .then(function (reply) {
            console.log("reply " + reply);
            return (reply);
        })
        .catch(console.log);

    };

并且此结果正确如 console.log 所示。 您对 for 循环的代码更改非常有效,但我认为它不能完美地满足我的需求。如果可以的话,我希望将它完全外包到模块文件中,这样我就可以在任何地方的类似情况下使用原型方法。这有可能吗? 我明白了,如果我也在 auth/ticket/ 路由器中创建一个带有 promise-redid 和 Q 的 Redis 客户端实例,这将导致具有两个 promise 支持的功能。 像这样:

var Q = require('q'),
promiseFactory = Q.Promise,
redis = require("promise-redis")(promiseFactory),
client;

然后是快速路线(单个文件中有很多路线),就像您的代码一样。

你明白我的意思吗?当然,您的解决方案完全可以满足我的需求,但如果可能的话,完全解决该工作的模块可能会更优雅。

与 redis、bluebird 和 typescript 一起使用:

import { RedisClient, createClient, ClientOpts } from "redis";
import { promisifyAll, PromisifyAllOptions } from "bluebird";


export module FMC_Redis {

    export class Redis {
        opt: ClientOpts;
        private rc: RedisClient;
        private rcPromise: any;

        private static _instance: Redis = null;
        public static current(_opt?: ClientOpts): Redis {

            if (!Redis._instance) {
                Redis._instance = new Redis(_opt);
                Redis._instance.redisConnect();
            }
            return Redis._instance;
        }

        public get client(): RedisClient {
            if (!this.rc.connected) throw new Error("There is no connection to Redis DB!");
            return this.rc;
        }

        /******* BLUEBIRD ********/
        public get clientAsync(): any {
            // promisifyAll functions of redisClient 
            // creating new redis client object which contains xxxAsync(..) functions.
            return this.rcPromise = promisifyAll(this.client);
        }

        private constructor(_opt?: ClientOpts) {
            if (Redis._instance) return;

            this.opt = _opt
                ? _opt
                : {
                    host: "127.0.0.1",
                    port: 6379,
                    db: "0"
                };
        }

        public redisConnect(): void {
            this.rc = createClient(this.opt);
            this.rc
                .on("ready", this.onReady)
                .on("end", this.onEnd)
                .on("error", this.onError);
        }

        private onReady(): void { console.log("Redis connection was successfully established." + arguments); }
        private onEnd(): void { console.warn("Redis connection was closed."); }
        private onError(err: any): void { console.error("There is an error: " + err); }


        /****** PROMISE *********/
        // promise redis test
        public getRegularPromise() {
            let rc = this.client;
            return new Promise(function (res, rej) {
                console.warn("> getKeyPromise() ::");
                rc.get("cem", function (err, val) {
                    console.log("DB Response OK.");
                    // if DB generated error:
                    if (err) rej(err);
                    // DB generated result:
                    else res(val);
                });
            });
        }


        /******* ASYNC - AWAIT *******/
        // async - await test function
        public delay(ms) {
            return new Promise<string>((fnResolve, fnReject) => {
                setTimeout(fnResolve("> delay(" + ms + ") > successfull result"), ms);
            });
        }

        public async delayTest() {
            console.log("\n****** delayTest ")
            let a = this.delay(500).then(a => console.log("\t" + a));

            let b = await this.delay(400);
            console.log("\tb::: " + b);
        }

        // async - await function
        public async getKey(key: string) {
            let reply = await this.clientAsync.getAsync("cem");
            return reply.toString();
        }
    }
}

let a = FMC_Redis.Redis.current();
// setTimeout(function () {
//     console.warn(a.client.set("cem", "naber"));
//     console.warn(a.client.get("cem"));
//     console.warn(a.client.keys("cem"));
// }, 1000);

/***** async await test client *****/
a.delayTest();


/** Standart Redis Client test client */
setTimeout(function () {
    a.client.get("cem", function (err, val) {
        console.log("\n****** Standart Redis Client")
        if (err) console.error("\tError: " + err);
        else console.log("\tValue ::" + val);
    });
}, 100)

/***** Using regular Promise with Redis Client > test client *****/
setTimeout(function () {
    a.getRegularPromise().then(function (v) {
        console.log("\n***** Regular Promise with Redis Client")
        console.log("\t> Then ::" + v);
    }).catch(function (e) {
        console.error("\t> Catch ::" + e);
    });
}, 100);

/***** Using bluebird promisify with Redis Client > test client *****/
setTimeout(function () {
    var header = "\n***** bluebird promisify with Redis Client";
    a.clientAsync.getAsync("cem").then(result => console.log(header + result)).catch(console.error);
}, 100);