两个要点通透Event Loop事件循环

·  阅读 1423

前言

事件循环过程:js是单线程,执行时根据压入执行栈的顺序进行执行。首先执行同步代码,遇到任务源时,分配到相应的任务队列中(宏任务和微任务),执行完同步任务后,检查执行栈是否为空,如果为空,检查微任务队列是否为空,如果不为空,则按照先进先出的方式一次性执行完微任务队列,如果为空,执行下一个宏任务。循环往复形成事件循环。过多不再赘述,进入正题。

要点

1.每次单个宏任务执行完后都会检查微任务队列是否为空
2.每次宏任务产生的微任务队列都是新创建的,宏任务队列只有一个

示例+解析(由易到难)

示例1:

console.log(1)

new Promise((resolve, reject) => {
    console.log(2)
    resolve()
}).then(res => {
    console.log(3)
})

console.log(4)
// 输出: 1 2 4
复制代码

解析:
-执行同步代码,输出1
-new Promise内部属于同步输出2
-.then推入微任务队列,之后执行代码输出4
-整个script代码块属于宏任务此时执行栈为空检查微任务队列,执行微任务输出3

示例2:

console.log('start')

setTimeout(() => {
  console.log('setTimeout')
}, 0)

new Promise((resolve) => {
  console.log('promise')
  resolve()
}).then(() => {
    console.log('then1')
  }).then(() => {
    console.log('then2')
  })

console.log('end')
// 输出: start promise end then1 then2 setTimeout
复制代码

解析:
-执行同步代码输出'start'
-遇到setTimeout放入宏任务队列,继续执行
-new Promise内部属于同步,输出promise
-之后的两个.then放入微任务队列,继续执行
-输出'end'
-执行栈为空 (同一次事件循环中,微任务永远在宏任务之前)
-检查微任务队列,输出'then1','then2'
-清空微任务队列后执行下一个宏任务
-输出setTimeout

示例3(示例2变种):

console.log('start')

setTimeout(() => {
  console.log('setTimeout')
  new Promise((resolve) => {
      console.log('promise2')
      resolve()
  }).then( () => {
      console.log('then3')
  })
}, 0)

new Promise((resolve) => {
  console.log('promise1')
  resolve()
}).then(() => {
    console.log('then1')
  }).then(() => {
    console.log('then2')
  })

console.log('end')
//输出 start promise1 end then1 then2 setTimeout promise2 then3
复制代码

解析:
-执行同步代码,输出'start'
-遇到setTimeout放入宏任务队列,继续向下执行
-new Promise内部属于同步,输出promise1
-之后的两个.then放入微任务队列,继续执行
-输出'end'
-检查微任务队列,输出'then1','then2'
-清空微任务队列后执行下一个宏任务
-输出setTimeout
-执行new Promise内部代码,输出promise2
-.then放入微任务队列
-执行栈为空,检查微任务队列,输出then3(注意:此时的微任务队列是新创建)

示例4(示例3的变种):

console.log('start')

setTimeout(() => {
  console.log('setTimeout1')
  new Promise((resolve) => {
      console.log('promise1')
      resolve()
  }).then( () => {
      console.log('then1')
  })
}, 0)

new Promise((resolve) => {
  console.log('promise2')
  resolve()
}).then(() => {
    console.log('then2')
  }).then(() => {
    console.log('then3')
  })
  
new Promise((resolve) => {
  console.log('promise3')
  resolve()
}).then(() => {
    console.log('then4')
  }).then(() => {
    console.log('then5')
  })
  

setTimeout(() => {
  console.log('setTimeout2')
  new Promise((resolve) => {
      console.log('promise4')
      resolve()
  }).then( () => {
      console.log('then6')
  })
}, 0)

console.log('end')
// 输出: start promise2 promise3 end then2 then4 then3 then5 setTimeout1 promise1 then1 setTimeout2 promise4 then6
复制代码

解析:
-执行同步代码,输出'start'
-遇到setTimeout放入宏任务队列,继续向下执行
-new Promise内部属于同步,输出promise2,继续执行
-之后的两个.then和下一个promise的.then交替放入微任务队列
-new Promise内部属于同步,输出promise3 -遇到setTimeout放入宏任务队列,继续向下执行 (注意:此处两个setTimeout被放入同一个宏任务队列)
-输出'end'
-检查微任务队列,输出then2 then4 then3 then5
-执行下一个宏任务(取出第一个setTimeout的回调执行)
-输出setTimeout1
-执行new Promise内部代码,输出promise1
-.then放入微任务队列
-执行栈为空,检查微任务队列,输出then1(新创建的微任务队列)
-执行栈为空,执行宏任务队列的下一个宏任务
-输出setTimeout2
-执行new Promise内部代码,输出promise4
-.then放入微任务队列
-执行栈为空,检查微任务队列,输出then6(新创建的微任务队列)
⚠️注意:该案列中有两个Promise,并且每个Proimise有两个.then。当含有多个Promise时,他们的then操作会交替执行,这是由于推入微任务队列的时机不同产生了这种现象

总结

理解两个要点,对应着再看代码输出,一步一步分析会发现事件循环也并不是那么的难。

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改