简述Js的事件循环

普通消息队列

一个代码块中的所有同步代码,都会被看作一个宏任务,添加到普通消息队列的尾部

延迟执行队列

当你在代码中使用 setTimeout 或 setInterval 时,会创建一个宏任务
延迟指定的时间后,将其放入延迟执行队列的尾部

事件循环系统

为了简化叙述,我们可以理解为:每一轮事件循环都执行一个宏任务,每一轮事件循环都关联一个微任务队列
具体过程如下:

  • 检查普通消息队列是否为空,若不为空,则从队列头部取出一个宏任务执行;否则,从延迟执行队列头部取出一个宏任务执行
  • 执行过程中如果遇到 setTimeout 或 setInterval,延迟指定时间后,将其作为一个宏任务 添加到延迟执行队列的尾部
  • 执行过程中如果创建了微任务(Promise 或者 MutationObserver),就将它添加到当前的微任务队列中
  • 执行当前微任务队列中的所有微任务
  • 开始下一个循环

例题1:

console.log('--开始--');

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

new Promise((resolve, reject) => {
  for (let i = 0; i < 5; i++) {
    console.log(i);
  }
  resolve()
}).then(()=>{
  console.log('Promise'); 
})

console.log('--结束--');

//---开始--
//0
//1
//2
//3
//4
//--结束--
//Promise
//timer1

第一轮循环:普通消息队列中只有一个宏任务(整个script)

  • 遇到console.log,直接输出 "--开始--"
  • 遇到setTimeout,因为指定时间为0ms,所以立即将其回调函数timer1添加到延迟执行队列的尾部
  • 遇到Promise的executor,直接执行,依次输出0、1、2、3、4,遇到resolve,将其回调函数添加到当前的微任务队列中
  • 遇到console.log,直接输出 “--结束--”
  • 检查微任务队列,发现一个微任务,立即执行,输出“Promise”

第二轮循环:普通消息队列为空,延迟执行队列中有一个宏任务(timer1)

  • 执行timer1的回调函数
  • 发现微任务队列为空,本轮循环结束

例题2:

new Promise((resolve) => {
  console.log('this is executor');
  resolve();
}).then(() => {
  console.log('this is resolve');
  setTimeout(() => { // 命名为timer1
    console.log('timer1');
  }, 4)
})

setTimeout(() => { // 命名为timer2
  console.log('timer2');
}, 4)

console.log('-- 结束--');

//this is executor
//-- 结束--
//this is resolve
//timer2
//timer1

第一轮循环:普通消息队列中只有一个宏任务(整个script)

  • 遇到Promise的executor,直接执行,输出“this is executor”
  • 遇到resolve,将其回调函数添加到当前的微任务队列中
  • 遇到setTimeout,在4ms后将timer2添加到延迟执行队列的尾部
  • 遇到console.log,直接输出 “--结束--”
  • 检查微任务队列,发现一个微任务,立即执行
  • 遇到console.log,直接输出 “this is resolve”
  • 遇到setTimeout,在4ms后将timer1添加到延迟执行队列的尾部,本轮循环结束

第二轮循环:普通消息队列为空,延迟执行队列头部为timer2

  • 执行timer2,输出timer2

第三轮循环:延迟执行队列头部为timer1

  • 执行timer1,输出timer1
原文地址:https://www.cnblogs.com/baebae996/p/14096938.html