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。
***************************************************
解决以上问题,需要注意两点
循环中异步函数操作下标,需要辅助的函数,将 i 作为参数传递。不要在循环中创建函数。
循环中异步函数组装数据,需要使用全局变量做计数器和数据接收容器。
看看改进的例子
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,组装数组完成'); } }); }
转载于:https://blog.51cto.com/soyoung/1550874