Nodejs, not waiting for Redis query to complete be

2019-09-04 05:34发布

问题:

Using Node.js I need to load three files dynamically with a require() function by fetching the file path from Cassandra. From each file I need to fetch data that is in Redis and do some validation before loading another file from Cassandra. The issue here is: before the validation logic executes and provides results the next file's start to get loaded in parallel. The validation result comes after the loading of the second file, which shouldn't happen. The second file loading should wait for the first file validation logic to be complete and must load only if the validation result is a success. Please help me ... How do I pause or wait for Redis to complete the query in node.js???

node.js

"use strict";
var express = require('express');
var cassandra = require('cassandra-driver');
var app = express();
var Promise = require('bluebird');
var redis = Promise.promisifyAll(require('redis'));
var redisClient = redis.createClient(6379, '127.0.0.1');
var client = new cassandra.Client({contactPoints: ['127.0.0.1'], keyspace: 'poc'});
client.execute("SELECT  file FROM testqry1", function (err, result) {
    if (!err){
        if ( result.rows.length > 0 ) {
            for(var i=0; i< result.rows.length; i++){
                var filePath=result.rows[i].get('file');
                var newdat=Promise.promisifyAll(require(filePath));
                var res = newdat(redisClient);
                console.log('res:::'+res);
                if (res=='failed'){
                    return;
                }
            }
        } else {
            console.log("No results");
        }
    }
});

file1.js

var crypto = require('crypto');
var redisValue='';
module.exports = function(redisclient){

redisclient.hmgetAsync("testdata", "text1").then(function(redisValue){
      console.log('value from redis::'+redisValue) 
  }).then(function(){
    var hashedUserID = crypto.createHmac('sha256', 'sample')
                   .update('helloworld')
                   .digest('hex'); 

    function disp(value) {
        console.log('value::'+value);
      }
      disp(hashedUserID);

      console.log('redisValue::'+redisValue);
      if(hashedUserID =='e043e7e68058c8a4cd686db38f01771bd7a04b8bb9a658d3cb40d0be45935094'){
        redata='true';
      }else{
        redata='false';
      }

      console.log('redata::'+redata)
})
}

file2.js & file3.js as same content

var result1='';
module.exports = function(redisclient){
    redisclient.hmget("testdata", "text1" , function(err, redisValue){  
      console.log('redisValue2 == %s',redisValue);
      if(redisValue == 'test value'){
        result1 = "success";
      }else{
        result1="failed";
      }
    });

    return result1;
} 

Output :

res:::undefined
res:::
res:::
value from redis::test data here
value::e043e7e68058c8a4cd686db38f01771bd7a04b8bb9a658d3cb40d0be45935094
redisValue::
redata::true
redisValue2 == test data here
redisValue3 == hello world test data

回答1:

You say that file2/3 are "same content" but they aren't in one critical area. Per Bluebird's documentation for promisifyAll (see http://bluebirdjs.com/docs/api/promise.promisifyall.html), this feature creates an ...Async version of each core function in the Redis client. You call hmgetAsync in your first case, but you only call hmget in your others.

This is important because you're using an async pattern but with a non-async code structure. In file2/3 you set result1 inside an async callback, but then return it below each call before the call could possibly have returned.

You have two choices:

1: You can convert file2/3/etc to a fully traditional pattern by passing in a callback in addition to the redis client:

module.exports = function(redisclient, callback){

Instead of returning result1, you would then call the callback with this value:

if(redisValue == 'test value'){
    callback(null, "success");
} else {
    callback("failed", null);
}

2: You could convert file2/3/..N to be Promise-based, in which case you do not need to promisifyAll(require(...)) them - you can simply require() them. Such a pattern might look like:

module.exports = function(redisclient){
    return redisclient.hmgetAsync("testdata", "text1");
};

This is a much simpler and cleaner option, and if you keep going with it you can see that you could probably even eliminate the require() and simply do the hmgetAsync in file1 with appropriate data returned by Cassandra. But it's hard to know without seeing your specific application needs. In any event, Promise-based patterns are generally much shorter and cleaner, but not always better - there IS a moderate performance overhead for using them. It's your call which way you go - either will work.