首页技术文章正文

在for循环中运行setTimeout的三种情况【黑马web前端】

更新时间:2019年07月26日 10时52分47秒 来源:黑马程序员论坛

在for循环中运行setTimeout是前端开发很常见的一种模式。最近被考了一道相关的题目觉得很有意思也很容易犯错,于是记录下来当做学习笔记。
下面先来看一段代码:

   for(var i=0;i<10;i++){
      setTimeout(console.log(i),0);
  }

看完这段代码思考一下结果输出是什么呢?
是不是第一时间想到的是连续的10个10呢?啊哈哈这样就错啦~
答案是0、1、2、3、4、5、6、7、8、9哦,这是因为很多人把这段代码与下面的代码混淆了

for(var i=0;i<10;i++){
     setTimeout(function(){
         console.log(i);  //连续的10个10
     },0);
}

这段代码输出结果是连续的10个10。
首先我们来说一下为什么是连续的10个10。因为在for循环中使用setTimeout涉及到了异步机制。说到异步机制那就要说一下JS的运行机制。
JS是单线程环境,也就是说代码的执行是从上到下,依次执行。也就是同一个时间只能做一件事。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。JavaScript将所有任务分成两种,一种是同步任务,另一种是异步任务。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

在所有同步任务执行完之前,任何的异步任务是不会执行的。 而setTimeout就是一个异步任务,所以会先执行for循环这个同步任务,把setTimeout()放进任务队列中等待主线程的for循环执行完毕,一旦"执行栈"中的所有同步任务执行完毕(循环结束后此时i=10)就会从队列中取出setTimeout()
for循环一次碰到一个 setTimeout(),并不是马上把setTimeout()拿到异步队列中,而要等到一秒后,才将其放到任务队列里面。

异步执行的运行机制如下:
所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
主线程不断重复上面的第三步。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会循环反复。
一般来说,有以下四种会放入异步任务队列:
setTimeout和setlnterval
DOM事件
ES6中的Promise
Ajax异步请求
回归正题,既然在for循环中运行setTimeout()涉及到了异步,那上面的解释不就解决问题了吗,可是为什么第一段代码结果不是连续的10个10呢?这就涉及到了console.log 与console.log()的区别了。
看下面代码:

看懂了吗,看懂了这道题也就解决了啊哈哈
其实console.log 与console.log()的区别就是 console.log()是立即执行的也就是IIFE,而console.log只是一个function 函数名。所以console.log()是同步任务跟for循环是同步执行的,而setTimeout()是异步任务需要等到主线程的同步任务执行完毕后才能执行,所以结果就是0、1、2、3、4、5、6、7、8、9了。

再来看一段代码:

for(var i=0;i<10;i++){
     setTimeout("console.log(i)",1000);//连续的10个10
}

啊哈哈,这时候结果就是连续的10个10了。

原因在于加了双引号的console.log()不再是立即执行函数。setTimeout() 会判断第一个参数是否是[function],如果不是,则会尝试将它当做字符串处理。也就是说,console.log(i)执行后的返回值转为字符串。

现在明白在for循环中运行setTimeout()的三种情况区别了吧

for(var i=0;i<10;i++){
     setTimeout(console.log(i),0);//0、1、2、3、4、5、6、7、8、9
}

for(var i=0;i<10;i++){
     setTimeout(function(){
         console.log(i);//连续的10个10
     },0);
}

for(var i=0;i<10;i++){
     setTimeout("console.log(i)",1000);//连续的10个10


推荐了解热门学科

java培训 Python人工智能 Web前端培训 PHP培训
区块链培训 影视制作培训 C++培训 产品经理培训
UI设计培训 新媒体培训 产品经理培训 Linux运维
大数据培训 智能机器人软件开发




传智播客是一家致力于培养高素质软件开发人才的科技公司“黑马程序员”是传智播客旗下高端IT教育品牌。自“黑马程序员”成立以来,教学研发团队一直致力于打造精品课程资源,不断在产、学、研3个层面创新自己的执教理念与教学方针,并集中“黑马程序员”的优势力量,针对性地出版了计算机系列教材50多册,制作教学视频数+套,发表各类技术文章数百篇。

传智播客从未停止思考

传智播客副总裁毕向东在2019IT培训行业变革大会提到,“传智播客意识到企业的用人需求已经从初级程序员升级到中高级程序员,具备多领域、多行业项目经验的人才成为企业用人的首选。”

中级程序员和初级程序员的差别在哪里?
项目经验。毕向东表示,“中级程序员和初级程序员最大的差别在于中级程序员比初级程序员多了三四年的工作经验,从而多出了更多的项目经验。“为此,传智播客研究院引进曾在知名IT企业如阿里、IBM就职的高级技术专家,集中研发面向中高级程序员的课程,用以满足企业用人需求,尽快补全IT行业所需的人才缺口。

何为中高级程序员课程?

传智播客进行了定义。中高级程序员课程,是在当前主流的初级程序员课程的基础上,增加多领域多行业的含金量项目,从技术的广度和深度上进行拓展“我们希望用5年的时间,打造上百个高含金量的项目,覆盖主流的32个行业。”传智播客课程研发总监于洋表示。




黑马程序员热门视频教程【点击播放】

Python入门教程完整版(懂中文就能学会) 零起点打开Java世界的大门
C++| 匠心之作 从0到1入门学编程 PHP|零基础入门开发者编程核心技术
Web前端入门教程_Web前端html+css+JavaScript 软件测试入门到精通


在线咨询 我要报名
和我们在线交谈!