Js是单线程语言
原因:作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。避免复杂同步问题。
消息队列
JS任务分类:
- 同步任务(synchronous)在主线程上排队执行的任务,前一个执行完毕,执行后一个任务。
- 异步任务(asynchronous)不进入主线程,进入任务队列(task queue),当任务队列通知主线程,某异步任务可以执行,该异步任务才会进入主线程执行。
console.log("A");
while(true){ }
console.log("B");
console.log("A");
setTimeout(function(){
console.log("B");
},0);
while(true){}
Event Loop
image.png- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 主线程之外,还存在一个任务队列(task queue)。只要异步任务有了运行结果,就在任务队列之中放置一个事件。
- 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
总结:主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。只要主线程空了,就会去读取任务队列。
image.png任务队列
- setTimeout和setlnterval
- DOM事件
- ES6中的Promise
- Ajax异步请求
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
浏览器渲染进程
GUI渲染进程
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。(前端性能优化点)
- 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
JS引擎线程
- 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
- 由于js是单线程(一个Tab页内中无论什么时候都只有一个JS线程在运行JS程序),依靠任务队列来进行js代码的执行,所以js引擎会一直等待着任务队列中任务的到来,然后加以处理。
- 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
事件触发线程
- 归属于渲染(浏览器内核)进程,不受JS引擎线程控制。主要用于控制事件(例如鼠标,键盘等事件),当该事件被触发时候,事件触发线程就会把该事件的处理函数添加进任务队列中,等待JS引擎线程空闲后执行
定时触发器线程
- setInterval与setTimeout所在线程。
- 浏览器的定时器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响计时的准确,因此通过单独的线程来计时并触发定时器,计时完毕后,满足定时器的触发条件,则将定时器的处理函数添加进任务队列中,等待JS引擎线程空闲后执行。
- W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
异步http请求线程
- 当XMLHttpRequest连接后,浏览器会新开的一个线程,当监控到readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进任务队列中,等待JS引擎线程空闲后执行。
宏任务(macrotask)与微任务(microtask)
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
JS中分为两种任务类型:macrotask和microtask,在ECMAScript中,microtask称为jobs,macrotask可称为task
image.png- macrotask:主代码块,setTimeout,setInterval等(可以看到,事件队列中的每一个事件都是一个macrotask)
- microtask:Promise,process.nextTick,mutation
- 补充:在node环境下,process.nextTick的优先级高于Promise,也就是可以简单理解为:在宏任务结束后会先执行微任务队列中的nextTickQueue部分,然后才会执行微任务中的Promise部分。
网友评论