JavaScript的事件驱动、异步等编程风格本身都非常受关注,再加上近来如火如荼的nodejs,使JavaScript这门“简单”的语言散发出迷人的光辉。

    在追逐这光亮的同时,也会遇到与我们以前编程不一样的地方,循环中的异步函数问题就是其中比较绕的一个。

var userlist = [{'id':'1','name':'张三'},{'id':'2','name':'李四'},{'id':'3','name':'王五'}]; 
var userlistlength = userlist.length;
var resultlist = [];

if(userlistlength > 0){
    for(var i = userlistlength - 1; i >= 0;i--){
        var usero = userlist[i];
        redis.get_redis_data(usero.id+'_msg',function(obj){
            usero.msg = obj.msg;
            usero.time =obj.time;
        
            resultlist.push(usero);
       });
        console.log('resultlist is :',resultlist);    
    }
}

 代码很明确,遍历一组用户,根据用户id去取所属该用户的消息,然后组装成一个json,并装入结果队列。

 

    但是,稍有异步编程经验的程序员一眼能看出或者感觉出,这段程序不会按我们预想的得出结果。最终打印结果为空。原因是,循环作为主线,不会等待里面异步函数执行完再往下走,3次循环都直接跳过异步函数,走到console时自然打印为空,并且这里的下标操作是无法取到对象的。 

 

 循环中的异步函数问题另一个例子,如下

var fs = require('fs'); 
  var files = ['a.txt', 'b.txt', 'c.txt']; 

  for (var i = 0 ; i < files.length; i++) { 
  fs.readFile(files[i], 'utf-8', function (err, contents) { 
  console.log(files[i] + ': ' + contents); 
  }); 
  }

同上,循环中的异步函数操作i下标,此时i已经是循环完成最后的值4,所以最终打印全是undefined。

 

***************************************************


解决以上问题,需要注意两点

  1. 循环中异步函数操作下标,需要辅助的函数,将 i 作为参数传递。不要在循环中创建函数。

  2. 循环中异步函数组装数据,需要使用全局变量做计数器和数据接收容器。

 

看看改进的例子

var userlist = [{'id':'1','name':'张三'},{'id':'2','name':'李四'},{'id':'3','name':'王五'}]; 
var userlistlength = userlist.length;

GlobalConfig.length = userlistlength;

if(userlistlength > 0){
    for(var i = userlistlength - 1; i >= 0;i--){
        loophelper(userlist[i]);
    }
    //这里即可得到循环后的结果
    console.log('resultlist is :'+ GlobalConfig.resultlist);
}


var loophelper = function(userobj){

    var resultlist = [];
    redis.get_redis_data(userobj.id+'_msg',function(obj){
            userobj.msg = obj.msg;
            userobj.time =obj.time;
        
            GlobalConfig.resultlist.push(userobj);
            GlobalConfig.length--;
            if(GlobalConfig.length === 0){
              console.log('end loop,组装数组完成');
            }
       });
}