首先来看一个比较简单的问题,我们想实现的就是每隔1s输出0-4的值,就是这么简单,看下错误写法:

function test() {
for (var i = 0; i < 5; ++i) {
setTimeout(function() {
console.log("index is :", i);
}, 1000);
}
}
test();

以上代码会如何输出?输出如下:

index is : 5
index is : 5
index is : 5
index is : 5
index is : 5

而且该操作几乎是在同一时间完成,setTimeout定时根本就没有起作用,这是因为:单线程的js在操作时,对于这种异步操作,会先进行一次“保存”,等到整个for循环执行结束后,此时i的值已经变成5,因为setTimeout是写在for循环中的,相当于存在5次定时调用,这5次调用均是在for循环结束后进行的,所以自然而然输出都是5,正确的实现有几种,一般情况下,我们使用递归实现,如下:

// var i = 0;
// var arr = [0, 1, 2, 3, 4]; // function box6() {
// if (i < arr.length) {
// setTimeout(function() {
// console.log("index is : ", i);
// i++;
// box6();
// }, 1000);
// }
// } box6(); function box7(param) {
if (param < 5) {
console.log("index is :", param);
setTimeout(function() {
box7(param + 1);
}, 1000)
}
}
box7(0);

正确实现每隔1s打印输出如下:

index is : 0
index is : 1
index is : 2
index is : 3
index is : 4

使用递归实现的倒计时:

function showTime(count) {
console.log("count is : ", count);
if (count == 0) {
console.log("All is Done!");
} else {
count -= 1;
setTimeout(function() {
showTime(count);
}, 1000);
}
} showTime(20);

递归调用很好的解决了setTimeout同时执行的情况,如果使用async、await实现,可以如下写法:

var asyncFunc = function(arr, i) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
arr.push(i);
console.log("index is : ", i);
resolve();
}, 1000);
});
} var box5 = async function() {
var arr = [];
for (var i = 0; i < 5; i++) {
await asyncFunc(arr, i);
}
console.log(arr);
} box5();

同样实现每隔1s正确地打印输出如下:

index is :  0
index is : 1
index is : 2
index is : 3
index is : 4
[ 0, 1, 2, 3, 4 ]

接下来再看个需求:构建一个函数数组,该数组每一项函数的功能是依次输出0-4,错误写法如下:

function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push(function() { console.log(item + ' ' + list[i]) });
}
return result;
} function testList() {
var fnlist = buildList([1, 2, 3]);
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
} testList();

输出如下:

item3 undefined
item3 undefined
item3 undefined

for循环里面使用匿名函数和直接写setTimeout调用比较类似,但是这里又有点不同,for循环执行结束后,匿名函数开始调用,发现里面存在“item”变量,这时依次会向上级查找,恰好找到循环结束时的item变量值为“list[2]”即为3,item为3但是i的值已经变为3,又因为list[3]的值为undefined,所以这里输出3遍item3 undefined。不信可以修改下数组如下,其余代码不变:

function buildList(list) {
...
} function testList() {
var fnlist = buildList([6, 7, 8]);
...
} testList();

这里绝对输出的是:

item8 undefined
item8 undefined
item8 undefined

再来看下正确的实现:

function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push(function(index, it) {
return function() {
console.log(it + ' ' + list[index]);
}
}(i, item));
}
return result;
} function testList() {
var fnlist = buildList([6, 7, 8]);
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
} testList();

输出如下:

item6 6
item7 7
item8 8

这里主要使用的是即时执行函数,什么是即时执行函数?可以理解为一个封闭的代码块,该代码块中的代码会在定义时立即执行一遍,各个代码块的作用域彼此独立,不会污染外部环境,写法其实有很多种,上面只是一种,同样的还有使用void、+、-、!等等,jquery源码就是直接使用的这里的圆括号写法的这种。

再看几个测试例子:

function box2() {
var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = (function(num) { //自我执行,并传参(将匿名函数形成一个表达式)(传递一个参数)
return num; //这里的num写什么都可以
})(i); //这时候这个括号里面的i和上面arr[i]的值是一样的都是取自for循环里面的i
}
return arr;
}
console.log(box2()); //[ 0, 1, 2, 3, 4 ]
function box4() {
var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = (function(num) { //自我执行,并传参(将匿名函数形成一个表达式)(传递一个参数),在这个闭包里面再写一个匿名函数
return function() {
return num;
}
})(i); //这时候这个括号里面的i和上面arr[i]的值是一样的都是取自for循环里面的i
}
return arr;
}
console.log(box4()); //[ [Function], [Function], [Function], [Function], [Function] ]

box4这种写法其实跟上面有一种是一致的,就不多说了,其实主要就是闭包,稍微改变一下代码,实现的结果却截然不同,共勉吧。。。

 

关于for循环里面异步操作的问题的更多相关文章

  1. JavaScript for循环里边异步操作问题。

    问题:(DRIVING.search是异步操作) for循环中做异步操作会导致aDistances数组里边的数据全部都是从A_SHOP_INFO数组中最后一条数据获取的值. var iIdx = 0; ...

  2. 6.Javascript如何处理循环的异步操作

    前沿:参考ES6语法的async/await的处理机制 先上一段代码 function getMoney(){ var money=[100,200,300] for( let i=0; i<m ...

  3. for循环中进行联网请求数据、for循环中进行异步数据操作,数据排序错乱问题解决;

    for循环中进行联网请求数据,由于网络请求是异步的,第一个网络请求还没有回调,第二次第三次以及后续的网络请求又已经发出去了,有可能后续的网络请求会先回调:这时我们接收到的数据的排序就会错乱:怎么才能让 ...

  4. C# 如何取消BackgroundWorker异步操作

    BackgroundWorker 在执行DoWork事件时该如何取消呢? 方法1 DoWork 执行一个(耗时)循环 方法2 DoWork执行一个(耗时)方法[注:方法没有循环] 见代码: 方法1中D ...

  5. 如何才能成为一个合格的web前端工程师

    转载原文地址:https://juejin.im/post/5cc1da82f265da036023b628 开篇前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快 ...

  6. Node_进阶_1

    第一天 1.1简介 Node.js简介 V8引擎本身就是用于Chrome浏览器的JS解释部分,Ryan Dahl把这个V8搬到了服务器上,用于做服务器的软件. Node.js是一个让Javascrip ...

  7. 前端003/【React + Mobx + NornJ】开发模式

    1.React + Mobx + NornJ 开发模式快速上手教程 github网址:https://github.com/joe-sky/nornj-cli/blob/master/docs/gui ...

  8. ES6-11学习笔记--异步迭代

    ES9提供异步迭代: for await of Symbol.asyncIterator   function getPromise(time) { return new Promise((resol ...

  9. Node.js的那些坑——如何让异步并发方法同步顺序执行(for循环+异步操作)

    1 前言 nodejs的回调,有时候真的是让人又爱又恨的,当需要用for循环把数据依次存入数据库,但是如果使用正常的for循环,永远都是最后一次值的记录,根本不符合要求. 解决此方案有几种,例如闭包( ...

随机推荐

  1. Android服务器——使用TomCat实现软件的版本检测,升级,以及下载更新进度!

    Android服务器--使用TomCat实现软件的版本检测,升级,以及下载更新进度! 算下来,TomCat服务器已经写了很长一段时间了,一直说拿他来搞点事 情,也一直没做,今天刚好有空,交流群还有人请 ...

  2. Linux - Bash shell的功能;内建命令type

    命令编修能力 (history): bash 的功能里头,相当棒的一个就是『他能记忆使用过的命令!』 这功能真的相当的棒!因为我只要在命令列按『上下键』就可以找到前/后一个输入的命令!而在很多 dis ...

  3. IOS 与ANDROID框架及应用开发模式对比一

    IOS 和ANDROID操作系统都是目前流行的移动操作系统,被移动终端和智能设备大量采用,两者都采用了先进的软件技术进行设计,为了方便应用开发两者都采用了先进的设计模式.两者在框架设计上都采用了什么技 ...

  4. 利用PreLoader实现一个平视显示(HUD)效果(可以运用到加载等待效果),并进行简单的讲解

    什么是PreLoader? PreLoader是由Volodymyr Kurbatov设计的一个很有意思的HUD(平视显示效果(Head Up Display)),通过运动污点和固定污点之间的粘黏动画 ...

  5. 如果以一个树状的形式返回一个UIView的所有子视图

    该方法也是从一个视频中看到,总觉得会有很大作用,故记录在这里. 它返回一个xml的字符串,用火狐浏览器或者其他可以格式化xml的工具打开,即可查看其层级关系. /** * 返回传入view的所有层级结 ...

  6. 和菜鸟一起学产品之用户体验设计UED

    ps:参考产品经理深入浅出ppt

  7. AngularJs 指令directive之require

    controller的用法分为两种情形,一种是require自定义的controller,由于自定义controller中的属性方法都由自己编 写,使用起来比较简单:另一种方法则是require An ...

  8. Android 在Fragment中执行onActivityResult不被调用的简单解决方法

    在Android开发中,我们经常会用到FragmentActivity下嵌套多个Fragment,但是在开发过程中会发现在嵌套的Fragment中使用onActivityResult回调方法没有被执行 ...

  9. 运行Applet程序

    [操作方法1:]① 编辑源程序welcome.java.② 编译程序 javac welcome.java③ 将Applet嵌入HTML网页.方法是,用记事本创建一个文件,文件内容如下:<app ...

  10. python自动重试第三方包retrying

    最近写了一个爬虫,需要连接国外的一个网站,经常出现掉线的情况,自己写了一个自动重连的代码,但感觉不够简洁... 后来就上万能的github,找到了一个第三包,基本能满足我的要求.这个第三方包就是ret ...