前言:由于浏览器中的事件循环机制与nodeJS中的事件循环机制不同,本文只记录基于浏览器的事件循环,希望能帮助到大家。
什么是异步?
我们知道 javascript 由于要和浏览器进行交互,所以从一开始它就被设计成了一门单线程非阻塞的脚本语言,
所谓单线程:即是任何时候,都只有一个主线程来处理所有的逻辑任务。
所谓非阻塞:即主线程不会直接处理异步任务,而是当它们返回结果之后再去处理,因为异步操作往往具有不确定性,会阻塞浏览器的渲染。
我们常用的,例如 ajax 请求、setTimeout、promise 都是异步操作
浏览器中的事件循环 event loop
为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。
有两类事件循环:一种针对浏览上下文(browsing context),还有一种针对worker(web worker)。
执行栈和事件队列
1.执行栈:
因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。
chrome浏览器Developer Tools 中的 performance 中的火焰图展示的便是执行栈的过程。
2.事件队列:
事件队列主要有宏任务队列和微任务队列
异步事件会由异步处理模块进行处理,返回处理结果后,加入与当前执行栈不同的其他队列
3.宏任务(task/macrotask):
可以有一个或多个 task 队列
script(全局任务,每一个js文件也是一个宏任务), setTimeout, setInterval, I/O, UI rendering.,setImmediate(属于nodeJS )。
4.微任务(microtask):
只有一个 microtask 队列
Promise, Object.observer, MutationObserver,process.nextTick(属于 nodeJS )。
事件循环执行顺序
1. event loop 执行步骤:
- 1、执行宏任务(先进先出),一次循环只执行一个宏任务)
- 2、执行栈 —— 同步方法顺序执行,异步方法交给异步处理模块
- 3、执行栈为空时取出微任务执行(先进先出),直到微任务队列为空
- 4、更新UI渲染。完成一轮循环,反复执行1-4。(不一定每次循环都会渲染)
2.update the rendering 渲染更新:
- 在一轮event loop中多次修改同一dom,只有最后一次会进行绘制。
- 渲染更新(Update the rendering)会在event loop中的tasks和microtasks完成后进行,但并不是每轮event loop都会更新渲染,浏览器有自己的机制来确定是否要更新渲染。如果在一帧(16.7ms)里多次修改了dom,浏览器可能只会渲染绘制一次。
- 如果希望在每轮event loop都即时呈现变动,可以使用requestAnimationFrame.
巩固练习
1.简单测试:
setTimeout(
function(){
console.log('1')
},0);
new Promise(
function(resolve){
console.log('2');
resolve()
}).then(
function(){
console.log('3');
});
console.log('4');
打印结果:
2
4
3
1
-
注意:
-
Promise 自身的代码是同步执行的,只有 .then后的回调函数才是微任务。
主线程的执行过程:
-
1、从宏任务队列(task)中取出 script,将所有同步代码推入执行栈中执行,遇到异步代码交给异步处理模块,异步处理模块处理完成后将任务按规则推入事件队列,宏任务推宏任务队列(先进先出),微任务推微任务队列(先进先出)。
所以输出 2 和 4
。 -
2、执行完 script 中的同步代码,再将微任务队列中最老的任务推入执行栈执行,直到清空微任务队列。
所以输出 3
。 -
3、浏览器更新渲染,再去宏任务队列中取出最老的任务推入执行栈中执行,循环以上步骤。
所以输出 1
。
2.进阶摸索
自己多写一些例子来练习。
这里有一篇非常好的英文的文章推荐给大家,有个交互演示非常直观明了
Tasks, microtasks, queues and schedules
还有一篇中文的文章,也有交互演示。
深入理解 JavaScript 事件循环(一)— event loop
3.深入探索
为什么setTimeout的执行时间并不够精确?
思考宏任务队列为什么可以有多个,而微任务队列只有一个?
深入探索 chrome dev Tools 的 performance。
nodeJS中的事件循环又是怎么实现的?
结语
有误请指正,希望共同进步。