WebAPIs-JS执行机制
1 - JS 是单线程
JavaScript语言的一大特点就是单线程,也就是说,同一时间只能做一件事。这是因为JavaScript这门脚本语言诞生的使命所致-------JavaScript是为了处理页面中用户的交互,以及操作DOM而诞生的。比如我们对某个DOM元素进行添加和删除操作,不能同时进行,应该先添加后删除。
浏览器中有三个常驻的线程,分别是JS引擎,界面渲染,事件响应。由于这三个线程同时要访问DOM树,所以为了线程安全,浏览器内部需要做互斥:当JS引擎在执行代码的时候,界面渲染和事件响应两个线程是被暂停的。所以当JS出现死循环,浏览器无法响应点击,也无法更新界面。现在的浏览器的JS引擎都是单线程的,尽管多线程功能强大,但是线程同步比较复杂,并且危险,稍有不慎就会崩溃死锁。单线程的好处不必考虑线程同步这样的复杂问题,简单而安全。
2 - 同步任务和异步任务
JS中所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
- 同步任务,指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
- 异步任务,指的是不进入主线程、而进入”任务队列”的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。
3 - JS执行机制(事件循环)
4 - 异步Ajax
JS是单线程的,当一个函数执行的时候,JS引擎会锁住DOM树,其他事件的响应代码只能在队列中等待,并且此时页面卡死。那么异步Ajax是怎么回事呢?一个常用的开发实践就是发起一个异步的Ajax,界面显示一个进度条样式的Gif,说好的单线程呢?事实上异步Ajax确实用了多线程,只是Ajax请求的Http连接部分由浏览器另外开了一个线程执行,执行完毕之后给JS引擎发送一个事件,这时候异步请求的回调代码得以执行。它的执行流程是这样的:
Http请求的执行在另外一个线程中,由于这个线程并不会操作DOM树,所以是可以保证线程安全的。发起Ajax请求和回调函数中间是没有JS执行的,所以页面不会卡死。
5 - 代码思考题
// 下面代码,为什么打印 1 2 3,而不是打印 1 3 2 呢?
console.log(1);
setTimeout(function () {
console.log(3);
}, 0);
console.log(2);
// 结果:1 2 3
// 首先,1、2 放在主队列,3 放在异步队列,主队列中的任务执行完成后再把异步队列中的任务 3 拿到主队列执行,所以打印 1 2 3
// 下面代码,打印什么?
console.log(1);
document.onclick = function() {
console.log('click');
}
setTimeout(function() {
console.log(3)
}, 3000)
console.log(2);
// 首先,1、2 放在主队列,click和3被放到异步队列中,最开始执行的肯定是1 2
// 如果在3s内点击文档,结果为:1 2 click 3,如果在3s之后点击文档,结果为:1 2 3 click。
6 - 真正的多线程
单线程导致的问题就是后面的任务等待前面任务完成,如果前面任务很耗时(比如读取网络数据),后面任务不得不一直等待!!
为了解决这个问题,利用多核 CPU 的计算能力,在HTML5中,引入了Web Worker这个概念。它能够在另外一个线程中执行计算密集的JS代码而不引起页面卡死,这是真正的多线程。然而为了保证线程安全,Worker中的代码是不能访问DOM的。其具体使用方法在此不作赘述,请参考:http://www.w3school.com.cn/html5/html_5_webworkers.asp