【问题标题】:node.js redis and how to use promise when using a modulenode.js redis以及使用模块时如何使用promise
【发布时间】:2015-08-19 10:59:13
【问题描述】:

我在节点服务器中有这样的 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();
};

所以我的经验是该模块能够正确地控制台记录结果。如果哈希有效,则返回 true,否则返回 false。这按预期工作。 问题是,for 循环在没有获取结果的情况下连续执行。我认为这是由竞争条件引起的。

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

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

this.client = redis.createClient();

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

请帮忙

【问题讨论】:

  • 你可以使用你的 Promise 库的 all() 函数。这将同时查找它们并在完成后返回结果。请注意,如果您需要查找很多内容,这可能会给 Redis 带来很大的负担。
  • 这也是我的情况。我需要平均查找 1 到 12 个哈希值。它是一个负载平衡系统。但我不知道如何正确实现它。在这种情况下,我从未使用过 promise it。
  • 也许你可以给我代码示例,你将如何处理这个案例。只需一步,我就可以开发它。

标签: javascript node.js redis promise q


【解决方案1】:

就像@brad 说的,你可以使用Q.all,它将一个promise 数组作为输入,然后在所有promise 完成后返回一个结果数组:

你的回答有误:

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

【讨论】:

  • 谢谢你们,你们的回答让他对 Promise 的使用有了最终的启发:-),太棒了。
  • 但是您是否也认识到我将所有 reds 函数外包给了模块文件(第一个代码块),该文件需要 promise-redid 并为 Q 构建工厂。
  • @gwinger 但在第二块 Redis.prototype.exists 仍然使用节点回调方式而不是承诺方式。
  • 您的更改似乎是正确的方式,但在返回:return Q.all(arry.map(self.exists.bind(self)) Q is not defined???不能明白这一点。嗯
  • ` Q = require("q")` 用于使用 Q Promise,arry.map(self.exists.bind(self) 也将返回一个 Promise 数组,bind 用于为 this 中的变量绑定正确的对象987654328@ 和 Q.all(...) 会做出承诺,等待所有承诺完成后再继续......
【解决方案2】:

@ mido22:但是你是否也认识到我将所有 reds 函数外包给模块文件(第一个代码块),它需要 promise-redid 并为 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;

然后是您的代码中的快速路线(每个文件中有很多更多路线)。

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-23
      • 2011-08-02
      • 2017-07-09
      • 2019-10-20
      • 2015-03-28
      • 2020-01-15
      相关资源
      最近更新 更多