Javascript单线程及定时器原理分析(1)
2015-11-15 13:25
423 查看
Javascript单线程及定时器原理分析(1)
原文写于 2014-07-28 https://github.com/kuitos/kuitos.github.io/issues/7Talk is cheap, code first.
说出代码运行结果
var i,a=0; setTimeout(function(){ console.log("timeout"); }, 1000); for(i=0;i<10;i++){ a += i; if(i === 9){ console.log("loop over"); } }
没错,当然是 loop over –> timeout
那么如果我们把timeout时间设为 0 呢,就像这样
// code1 setTimeout(function(){ console.log("timeout"); }, 0);
结果还是 loop over –> timeout !!
我们假设你已经知道不同浏览器对setTimeout都有个最短时间间隔这个事实,比如chrome的定时器最短时间间隔是4ms。
这个时候你可能会说for循环先执行完且时间短于4ms,所以顺序是 loop over –> timeout.
假如我们把循环放大, 就像这样
// code2 for(i=0;i<1000000;i++){ a += i; if(i === 999999){ console.log("loop over"); } }
结果却还是 loop over –> timeout.
你可能会说 1000000 次循环时间小于4ms, 好吧我们假设chrome已经如此这般牛x.
那如果我们把完整代码改成这样呢
//code 3 var i,a=0,begin; setTimeout(function(){ console.log("timeout"); }, 1000); console.log(begin=Date.now()) while(true){ if(Date.now()-begin>1500){ console.log("loop over"); break; } } console.log(Date.now()); console.log("WTF!");
WTF!循环时间超过1000ms的情况下结果依然是 loop over –> WTF –> timeout !!
会出现这种结果的一起原因归结于 Javascript是单线程的,所以导致 javascript的定时器从来都不是可靠的,当JS引擎的线程一直很忙的时候,它的定时器是永远不会执行的。只有线程空闲下来的,才有功夫去玩你的定时器。看这段代码
// code4 var a = 0; setTimeout(function(){ console.log("timeout"); }, 1000) while(true){ a++; }
没错,timeout永远都不会打印出来。(如果你不幸在浏览器中运行了这段代码,请调出进程管理器,kill process ….)。
假如你有过一定的js代码经验,你一定会问,既然是单线程的,那么javascript中的异步是怎么实现的呢,那些所谓的回调又是几个意思,用来装x的专业术语而已?
Javascript中的异步是基于 event-driver(事件驱动) 的,几乎所有的单线程语言都是通过这种方式实现异步的。什么是异步呢,言简意赅的说,异步函数就是会导致将来运行一个取自事件队列的函数的函数。
事件队列又是什么东西呢??
我们举个例子说明,假设:我们处于一个页面,这个页面上有一个setTimeout正在执行延时1000ms执行某段代码;而在这个200ms的时候,我们点击了一个按钮,因为此时已经满足事件触发条件,且JavaScript线程空闲,所以按照我们的脚本浏览器会立即执行与这个事件绑定的另外某段代码;点击事件触发的某段代码会做两件事,一件事是注册一个setInterval要求每隔700ms执行某段代码;另一件是发送一个ajax请求,并要求请求返回后执行某段代码,这个请求会在1500ms后返回。在这之后,可能还会有其它的事件被触发。
那么在整个事件队列中,他是如下排列的:
这时候我们可以再来试着解释一下code3那段代码的执行机制是怎样的。
在此之前有一点你必须要明确,Javascript中函数是一等公民,所有代码块的运行都是基于函数的!
code4代码处于全局环境,所以我们可以将它一整个代码块理解成一个 立即执行的匿名函数, 就像这样
// code5 (function(){ var i,a=0; setTimeout(function(){ console.log("timeout"); }, 1000); for(i=0;i<100000;i++){ a += i; if(i === 99999){ console.log("loop over"); } } console.log("WTF!"); })();
那么js引擎解释这段代码时过程是这样的:
发现一个匿名函数,将该函数加入到事件队列中。执行匿名函数
执行到setTimeout方法,构建一个定时器,将定时器加入到事件队列中,此时队列顺序为: 匿名函数 –> 定时器回调
js引擎线程继续处理匿名函数,依次输出 loop over –> WTF
匿名函数执行完毕,发现队列里还有一个定时器,于是执行定时器回调。
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- JavaScript 各种遍历方式详解
- call/apply/bind 的理解与实例分享
- 如何创建对象以及jQuery中创建对象的方式
- IE8开发人员工具教程(二)
- Mootools 1.2教程 定时器和哈希简介
- 在flex中执行一个javascript方法的简单方式