Node.js 和 Redis;等待循环完成

Posted

技术标签:

【中文标题】Node.js 和 Redis;等待循环完成【英文标题】:Node.js & Redis; Waiting for a loop to finish 【发布时间】:2012-09-30 22:33:39 【问题描述】:

我想问这​​个问题,因为我不确定我的 Node.js 逻辑是否正确

我有一组 id 需要使用 redis 的 get 方法查询。在检查了某个值之后(假设我正在检查我使用给定“键”获得的对象是否具有空名称),我将它们添加到列表中。这是我的示例代码;

var finalList = [];
var list = [];
redisClient.smembers("student_list", function(err,result)
            list = result; //id's of students
            console.log(result);

            var possibleStudents = [];


            for(var i = 0; i < list.length; i++)


                redisClient.get(list[i], function(err, result)
                    if(err)
                        console.log("Error: "+err);
                    else
                        tempObject = JSON.parse(result);
                        if(tempObject.name != null)
                            finalList.push(tempObject);
                        
                    
                );     
            

    );
   console.log("Goes here after checking every single object");

但正如预期的那样,由于 Node 的异步特性,它在不检查列表中的每个 id 的情况下执行“Goes here...”。我需要在检查每个 id 后应用其余程序(在 redis db 中映射并检查名称)。但我不知道该怎么做。也许我可以将回调附加到 for 循环并确保我的其余函数在循环完成后开始运行(我知道这是不可能的,但只是提供一个想法)?

【问题讨论】:

【参考方案1】:

我会按照您在问题中建议的路线并将自定义回调附加到您的获取函数:

function getStudentsData(callback) 
    var setList = [];
    var dataList = [];

    redisClient.smembers("student_setList", function(err,result) 
        setList = result; //id's of students

        for(var i = 0; i < setList.length; i++) 
            redisClient.get(setList[i], function(err, result) 
                if(err) 
                    console.log("Error: "+err);
                 else 
                    tempObject = JSON.parse(result);
                    if(tempObject.name != null) 
                        dataList.push(tempObject);
                    
                
            );     
        

        if(dataList.length == setList.length) 
            if(typeof callback == "function") 
                callback(dataList);
            
            console.log("getStudentsData: done");
         else 
            console.log("getStudentsData: length mistmach");
        

    );


getStudentsData(function(dataList) 
    console.log("Goes here after checking every single object");
    console.log(dataList.length);
    //More code here
);

这可能是最有效的方法;或者,您可以依靠老式的while 循环,直到数据准备好:

var finalList = [];
var list = [0];

redisClient.smembers("student_list", function(err,result) 
    list = result; //id's of students
    var possibleStudents = [];

    for(var i = 0; i < list.length; i++) 
        redisClient.get(list[i], function(err, result) 
            if(err) 
                console.log("Error: "+err);
             else 
                tempObject = JSON.parse(result);
                if(tempObject.name != null) 
                    finalList.push(tempObject);
                
            
        );     
    
);


process.nextTick(function() 
    if(finalList.length == list.length) 
        //Done
        console.log("Goes here after checking every single object");
        console.log(dataList.length);
        //More code here
     else 
        //Not done, keep looping
        process.nextTick(arguments.callee);
    
);

我们使用process.nextTick 而不是实际的while 来确保其他请求在此期间不会被阻止;由于 javascript 的单线程特性,这是首选方式。为了完整起见,我将其加入,但前一种方法更有效,更适合 node.js,所以除非涉及重大重写,否则请继续使用。

这两种情况都依赖异步回调毫无价值,这意味着它之外的任何代码仍然可能在其他代码完成之前运行。例如,使用我们的第一个 sn-p:

function getStudentsData(callback) 
    //[...]


getStudentsData(function(dataList) 
    //[...]
);

console.log("hello world");

最后一个 console.log 几乎可以保证在我们传递给 getStudentsData 的回调被触发之前运行。解决方法?为它设计,这就是 node.js 的工作方式。在我们上面的例子中,这很简单,我们只需在传递给 getStudentsData 的回调中调用 console.log only,而不是在它之外。其他场景需要与传统过程编码稍有不同的解决方案,但是一旦您了解它,您就会发现事件驱动和非阻塞实际上是一个非常强大的功能。

【讨论】:

第一个例子似乎对我不起作用 :(。见this,redis 调用是异步的。我的results 的控制台日志将首先显示,因为它的逻辑完全相同就像你的例子一样,嗯。【参考方案2】:

试试finish 模块。我创建了这个模块来处理这个问题。它比 Async 更易于使用,并且性能也更好。这是一个例子:

var finish = require("finish");
finish(function(async)  
  // Any asynchronous calls within this function will be captured
  // Just wrap each asynchronous call with function 'async'
  ['file1', 'file2', 'file3'].forEach(function(file) 
    async(function(done)  
      // Your async function should use 'done' as callback, or call 'done' in its callback
      fs.readFile(file, done); 
    );
  );
, function(err, results) 
  // fired after all asynchronous calls finish or as soon as an error occurs
  console.log(results[0]);console.log(results[1]);console.log(results[2]);
);

【讨论】:

应该发布代码示例以及链接。这样,如果链接失效,帖子就没有用了。 谢谢。添加了代码示例。 警告:如果forEach 的数组为空,finish 当前会阻塞,并以“TypeError: cannot read property 'kickoff' of undefined”退出。我花了一段时间才找到它;希望它可以减轻别人的痛苦! See issue on Github. @OllieFord 问题已修复。感谢您报告此事。 这是天赐之物……我的主。谢谢!!【参考方案3】:

尝试使用 node.js 的 async 模块。该模块具有异步 forEach。

【讨论】:

以上是关于Node.js 和 Redis;等待循环完成的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 等待循环中读取的所有文件

Node.js 等待循环中读取的所有文件

node js redis循环遍历每个哈希键值

理解 node.js 的事件循环

Node.js/Mongoose 等待异步 For 循环

JavaScript使用事件循环在调用堆栈