浏览器中每个一个窗口都是一个单独的进程。这就需要分析浏览器与Javascript解释引擎之间的关系。先给出结论,浏览器本身是多线程的,Javascript解释引擎是单线程的。
先说说浏览器有哪些线程,可以从其功能上分析,浏览器针对Javascript需要支持解释执行、响应事件、渲染UI、下载资源等。可见,浏览器至少需要4个线程,我们着重分析跟Javascript有关的3个线程,解释器线程、交互线程(事件触发线程)、GUI线程。
JS单线程、异步、同步概念
众所周知,JS是单线程(如果一个线程删DOM,一个线程增DOM,浏览器傻逼了~所以只能单着了),虽然有webworker酱紫的多线程出现,但也是在主线程的控制下。webworker仅仅能进行计算任务,不能操作DOM,所以本质上还是单线程。
任务队列
js运作在浏览器中,是单线程的,js代码始终在一个线程上执行,此线程被称为js引擎线程, javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。每一段JS程序都可以看做是一个任务。
因为js引擎是单线程的,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在JS引擎上排队执行的任务。异步任务指的是,不进入JS引擎、而进入"任务队列"(task queue)的任务。
任务队列又分为 宏任务队列和微任务队列
宏任务队列:setTimeout、setInterval、 setImmediate(Node)、requestAnimation(浏览器)、IO、UI rendering
微任务队列:Promise.then、Object.observe、MutationObserver
Event Loop(事件循环)
Event Loop主要包含三大块 :一个函数执行站、一个宏任务队列和一个微任务队列。
在JS引擎上的任务,只有前一个任务执行完毕,才能执行后一个任务,当JS引擎中的任务执行完成了,就会去查询异步的任务队列中是否有可以执行的任务,一旦这些异步任务可以执行了,就会将它添加到JS引擎中,以此循环。由于JS引擎从“任务队列”中读取事件的这个过程是不断循环的,所以整个的这种运行机制又称为 Event Loop(事件循环)。
在执行栈上的任务,只有前一个任务执行完毕,才能执行后一个任务,当执行栈上的任务执行完毕之后,检查微任务队列,取出队列中所有事件压入执行栈执行,完毕之后,检查宏任务队列,取出一个事件压入执行栈执行,完毕之后,再次检查微任务队列,取出所有的事件压入执行栈执行,完毕之后,检查宏任务队列,取出一个事件压入执行栈执行,重复该过程。这整个过程是不断循环执行,所以这种运行机制又称未Event Loop(事件循环)。
注意:对于宏任务每次只从宏任务队列种取一个事件压入执行栈执行,微任务每次从微任务队列种取出所有的事件压入执行栈执行。
任务队列数据来源
GUI事件触发线程:JavaScript引擎脚本的执行不影响html元素事件的触发,点击、放大、拖拽浏览器或DOM元素,触发线程捕捉对应的回调函数,添加到任务队列末尾。
定时触发线程::当定时时刻达到的时候,定时线程会把对应的函数添加到任务队列末尾。
HTTP异步请求线程:请求线程执行完毕之后 ,会把对应的函数(success、error)添加任务队列末尾。
最后来个练习题
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
Promise.resolve().then(function() {
console.log('setTimeout-Promise1');
return 1
}).then(function() {
console.log('setTimeout-Promise2');
})
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
return 1
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
});
setTimeout(function() {
console.log('setTimeout2');
}, 0);
console.log('script end');
参考资料:
https://www.cnblogs.com/xhz-dalalala/p/5661955.html
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E7%89%9B/26456.shtml
https://www.cnblogs.com/tesky0125/p/4619549.html
http://www.cnblogs.com/hity-tt/p/6733062.html
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/