单线程和异步 Event Loop
1.单线程
什么是单线程?和异步有什么关系
单线程:就是在同一时间只能做一件事情。
为什么使用单线程?
为了避免DOM渲染的冲突。(因为浏览器需要渲染DOM,JS也可以修改DOM,一个渲染节点另一个把节点删除了,那到底怎么弄)
HTML5 提出 websorker支持多线程,但是也不能访问DOM从这方面来说也验证了这个说法。
(JS执行浏览器DOM渲染暂停)也就是说JS执行的优先级高于浏览器渲染
两个JS不能同时执行(都修改DOM就冲突了)
单线程的问题
单线程意味这所有的任务都需要排队,执行完一个才能继续执行下一个,但是如果前一个执行很长,后一个任务就需要一直等待。
下面的例子就是单线程 同步的操作:必须从头到尾执行
//循环太多 卡顿
var i,sum = 0;
for(i = 0; i < 100000000; i++){
sum += 1;
}
console.log(sum)
//alert
alert(1);
console.log(2);
alert(3)
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
2.解决方案 - 异步
先看看异步:我们常用的setTimeout,setInterval,ajax请求都属于异步操作
主线程完全可以不管I/O设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
$.ajax({
url:'./demo.js',
success: function(result){
console.log(result)
}
})
console.log(300)
//100
//300
//200或请求的数据
异步是如何实现的?
JS中所有的任务我们可以把他分为两种,一种是同步任务,一种是异步任务。
整个任务的执行机制是:
- 将所有的同步任务放在主线程上,形成一个执行栈
- 将所有的异步任务都挂起,当达到条件的时候就会放在‘任务队列’中(监听事件,触发事件)
- 执行主线程上的所有同步任务,系统就会在’任务队列’中查看还有那些任务,异步任务结束等待状态,进入执行栈,开始执行
- 系统不断的在主线程和任务列队之间轮询重复上面的第3步奏
3.Event Loop
主线程从‘任务队列’中读取事件,整个不断循环的过程叫 Event Loop (事件轮询)
就上上面所说的步奏3:
主线程上的所有同步任务执行完后,系统就会在,异步任务结束等待状态,进入执行栈,开始执行.(栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数)
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
还是这个例子:setTimeout会在1秒的时候(达到条件)放入任务队列中,在被系统轮询到,在丢到主线程中然后执行打印
说说异步的缺点
-
没有按照书写方式执行,可读性差
-
callback 不容易模块化
这也就有了后面的ES6标准正式提出的Promise做了铺垫
延伸阅读:[阮一峰老师的 JavaScript 运行机制详解:再谈Event Loop]http://www.ruanyifeng.com/blog/2014/10/event-loop.html