blog
blog copied to clipboard
浏览器原理 - JavaScript 的 EventLoop
Javascript的事件环,主要就在理解宏任务
和微任务
这两种异步任务
任务类型 | 事件类型 | 优先级 |
---|---|---|
宏任务 | setTimeOut 、 setInterval 、 setImmediate 、 I/O 、 各种callback 、 UI渲染 、messageChannel 等 |
主代码块 > setImmediate > postMessage > setTimeOut /setInterval |
微任务 | process.nextTick 、Promise 、MutationObserver 、async(实质上也是promise) |
process.nextTick > Promise > MutationOberser |
我们常常把EventLoop
中分为 内存
、执行栈
、WebApi
、异步回调队列
(包括微任务队列和宏任务队列)
简单用代码表示一下过程
for (macroTask of macroTaskQueue) {
// 1. Handle current MACRO-TASK
handleMacroTask();
// 2. Handle all MICRO-TASK
for (microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
执行流程
1️⃣ Javascript
内核加载代码到执行栈
2️⃣ 执行栈
依次执行主线程的同步任务
,过程中若遇调用了异步Api则会添加回调事件到回调队列
中。且微任务
事件添加到微任务队列中,宏任务
事件添加到宏任务队列中去。直到当前执行栈
中代码执行完毕。
3️⃣ 开始执行当前所有微任务队列
中的微任务回调事件。 (:smirk:注意是所有哦,相当于清空队列)
4️⃣ 取出宏任务队列
中的第一条(先进先出原则哦)宏任务,放到执行栈
中执行。
5️⃣ 执行当前执行栈
中的宏任务,若此过程总又再遇到微任务
或者宏任务
,继续把微任务
和宏任务
进行各自队伍的入队
操作,然后本轮的宏任务
执行完后,又把本轮产生的微任务
一次性出队都执行了。
6️⃣ 以上操作往复循环...就是我们平时说的eventLoop
了
综合一下....特点是
⭕️ 微任务队列操作,总是会一次性清空队列 ⭕️ 宏任务队列每次只会取出一条任务到执行栈中执行
代码实例
let promiseGlobal = new Promise(resolve=>{
console.log(1)
resolve('2')
})
console.log(3)
promiseGlobal.then(data=>{
console.log(data)
let setTimeoutInner = setTimeout(_=>{
console.log(4)
},1000)
let promiseInner =new Promise(resolve=>{
console.log(5)
resolve(6)
}).then(data=>{
console.log(data)
})
})
let setTimeoutGlobal = setTimeout(_=>{
console.log(7);
let promiseInGlobalTimeout = new Promise(resolve=>{
console.log(8);
resolve(9)
}).then(data=>{
console.log(data)
})
},1000)
建议不要直接拷贝到 控制台跑...大家先想想:smirk:
过程动画
答案
1 3 2 5 6 __ 等待一秒___ 7 8 9 4
例2
let mc = new MessageChannel();
let p1 = mc.port1, p2 = mc.port2;
setTimeout(function(){
let pro2 = new Promise(resolve=>{
resolve()
})
pro2.then(data=>{
console.log('pro2');
})
},0)
console.log('first round');
p1.onmessage = function(data){
let pro3 = new Promise(resolve=>{
resolve()
})
pro3.then(data=>{
console.log('pro3');
})
console.log(data);
}
p2.postMessage("message form port2")
p2.postMessage("message form port2 second")
let pro1 = new Promise((solve)=>{
console.log('pro1 inner');
solve()
})
pro1.then(data=>{
console.log('pro then')
});
结果
其他问题
Q: 我的setTimeout
函数到时间了,为啥一直不去执行。
A: setTimeOut
的回调会被放到任务队列中,需要当前的执行栈执行完了,才会去执行执行任务队列中的内容。出现setTimeout
回调不及时,说明在执行栈中出现了阻塞,或者说执行代码过多。
vue.$nextTick
A:常见的vue.$nextTick
会把事件直接插入到当前微任务
队列的中
参考资料
[1] 实现异步的Api
[2] vue 的nextTick
[3] vue 的DOM更新机制