blog icon indicating copy to clipboard operation
blog copied to clipboard

浏览器原理 - JavaScript 的 EventLoop

Open HXWfromDJTU opened this issue 4 years ago • 0 comments

Javascript的事件环,主要就在理解宏任务微任务这两种异步任务

任务类型 事件类型 优先级
宏任务 setTimeOutsetIntervalsetImmediateI/O 、 各种callbackUI渲染messageChannel 主代码块 > setImmediate > postMessage > setTimeOut/setInterval
微任务 process.nextTickPromiseMutationObserverasync(实质上也是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更新机制

[4] 高能福利 -- 外国帅哥讲清楚Eventloop

HXWfromDJTU avatar Oct 07 '20 07:10 HXWfromDJTU