了解浏览器端的event loop

878 阅读3分钟

浏览器端

什么是event loop

我认为这是js单线程的一种事件调度机制,保证代码能够有序运行。 浏览器环境下,只需要知道调用堆栈call stack一个宏任务队列一个微任务队列即可。

执行规则如下:

  1. shift第一个宏任务(也就是当前script上下文)
let temp = 宏队列.length && 宏队列.shift()
  1. 将其压入栈中,并执行(包括声明函数,声明promise,赋值,运算等同步操作)
if(temp) stack.exe(temp)
  1. 堆栈执行完为空,检查微任务队列并执行,
while(微队列.length){
  let temp = 微队列.shift()
  stack.exe(temp)
}
  1. 当微任务队列为空,重复第一步,此时就成了一个loop
  2. stack.exe()期间,可能会执行
宏队列.push()
微队列.push()

event loop中的Promise

  • 浏览器环境下promise回调函数属于微任务
  • new Promise里的构造函数是同步执行的
  • then回调函数理解为注册在其promise内部
  • then回调函数只有在promise被决议后,才会推入微任务队列的末尾
  • then(xxx).then(xxx)第一个then回调函数在堆栈中执行完毕后,返回一个新的promise,值为第一个then函数的返回值(可能是同步值,可能是promise)
  • 如果第一个then返回值是promise,那第二个then函数何时被推入队列末尾,取决于这个 promise何时解决
  • 如果是非promise,那这个promise相当于Promise.resolve(),那这个then回调函数会立即推入到微任务队列末尾
new Promise((resolve,reject)=>{
  console.log('同步执行')
  resolve(1)//将当前promise的状态设为fulfilled,值设为1(等待then函数使用)
}).then()
//遇到then函数,首先判断promise的状态是否解决,
//如果解决,将 then里的回调函数添加到微任务队列的末尾
//如果没有解决,只是注册,当状态从pending变更为解决时,再将此回调函数添加到微任务队列的末尾

真题

setTimeout(
  ()=>{console.log(0)}// push一个宏任务
)

new Promise((resolve,reject)=>{
  console.log(1)
  resolve()
}).then(
  /*P1.then start*/
  ()=>{
    console.log(2)
    new Promise((resolve,reject)=>{
      console.log(3)
      resolve()
    }).then(
      ()=>{console.log(4)}// P3.then
    ).then(
      ()=>{console.log(5)}// P3.then.then
    )
  }
  /*P1.then end*/
 ).then(
   ()=>{console.log(6)}// P1.then.then
)

new Promise((resolve,reject)=>{
  console.log(7)
  resolve() 
}).then(
  ()=>{console.log(8)}// P2.then
)

解答:

  • 第一个宏任务:

    1. 执行setTimeout函数,宏任务.push(()=>{console.log(0)})
    2. new Promise执行构造器函数,console.log(1)
    3. resolve() 微任务.push(P1.then)
    4. P1.then.then注册到P1.then上,等待执行
    5. 又一个new Promise执行构造器函数,console.log(7)
    6. resolve() 微任务.push(()=>{console.log(8)})
  • 第一个宏任务执行完毕,检查微任务队列 [ P1.then , ()=>{console.log(8)} ]

    1. 执行第一个微任务P1.then函数,console.log(2)
    2. new Promise执行构造器函数,console.log(3)
    3. resolve() 微任务.push(P3.then)
    4. P3.then.then注册在P3.then()上,等待执行
    5. P1.then执行完毕,返回undefined,则微任务.push(P1.then.then)
    6. 此时微任务队列[ ()=>{console.log(8)} , P3.then , P1.then.then ]
    7. 执行第一个微任务,console.log(8)
    8. 执行P3.then函数,console.log(4)
    9. P3.then函数执行完毕,返回值为undefined,微任务.push(P3.then.then)
    10. 此时微任务队列[ P1.then.then , P3.then.then ]
    11. 执行 P1.then.then函数 console.log(6)
    12. 执行 P3.then.then函数 console.log(5)
  • 微任务队列为空,开启下一轮loop

    1. 宏任务队列首个宏任务压入堆栈执行,console.log(0)

结果

172384650

题目拓展

new Promise((resolve,reject)=>{
  console.log(1)
  resolve()
}).then(()=>{
  console.log(2)
  return new Promise((resolve,reject)=>{
    console.log(3)
    resolve()
  }).then(()=>{
    console.log(4)
  }).then(()=>{
    console.log(5)}
  )
}).then(()=>{
  console.log(6)
})