浏览器的事件循环EventLoop

浏览器一帧做哪些事情

  1. js执行
  2. requestAnimationFrame回调
  3. 渲染
  4. requestIdleCallback回调

事件循环主要指的是js执行过程的事件循环

  1. 宏任务:setTimeout,setInterval,messageChannel,js代码块,输入输出操作,事件回调
  2. 微任务:Promise.then,.catch,.finally, async await, MutationObverser, queueMicrotask
  3. 动画队列:Animation callbacks, requestAnimationFrame

上代码

js 复制代码
console.log('1');

const channel = new MessageChannel();
channel.port1.onmessage = () => {
  console.log('2');
  setTimeout(() => console.log('3'), 0);
};
channel.port2.postMessage(null);

setTimeout(() => {
  console.log('4');
   Promise.resolve().then(() => console.log('5'));
}, 0);

async function async1() {
  console.log('6');
  await async2();
  console.log('7');
}

async function async2() {
  console.log('8');
  await Promise.resolve().then(() => console.log('9'));
  console.log('10');
}

Promise.resolve().then(() => {
  console.log('11');
});

async1();

new Promise(resolve => {
  console.log('12');
  resolve();
}).then(() => console.log('13'));

console.log('14');

解析执行过程

同步代码执行

  1. 输出1
  2. 产生messageChannel宏任务,放入宏任务队列
  3. 产生setTimeout宏任务,放入宏任务队列
  4. 产生.then微任务,放入微任务队列
  5. 执行async1函数,输出6 , 执行async2函数,输出8,产生一个微任务,放入微任务队列
  6. 返回继续执行同步代码,输出12, 产生一个微任务,放入微任务队列
  7. 输出14

同步代码执行完毕,处理微任务队列

  1. 输出11
  2. 输出9,产生新的微任务,放入微任务队列
  3. 输出13
  4. 将新产生的微任务取出执行,输出10
  5. 此时async2执行完毕,将saync2后面的代码放入微任务队列
  6. 取出微任务队列任务执行,输出7

微任务代码全部执行完毕,处理宏任务

  1. 取出messageChannel宏任务执行,输出2,产生宏任务,放入宏任务队列
  2. 取出setTimeout的宏任务执行,输出4,产生微任务,放入微任务队列
  3. 取出微任务执行,输出5
  4. 取出宏任务执行,输出3

总结

  1. messageChannel和setTimeout在浏览器端没有优先级顺序,谁先进入宏任务队列,谁先执行
  2. async,await的执行,await后面的代码同步执行,await下一行的代码,等到Promise状态变更以后,再放入微任务队列中
  3. 如果代码中嵌入了requestAnimationFrame回调,那么执行顺序将变得不固定,原因是得看浏览器当前正在处理一帧内的哪个阶段
  • 如果是js执行阶段,那么就会先执行setTimeout1和setTimeout2,再输出rAF
  • 如果是js执行阶段,并且js执行的剩余时间不太够的情况下,会先输出setTimeout1, 然后输出rAF,再下一帧的js执行阶段,再输出setTimeout2
  • 如果当前是requestAnimationFrame阶段,那么会先输入rAF,再下一帧输出setTimeout1和setTimeout2

验证结论1

验证结论3

js 复制代码
setTimeout(() => {
  console.log('setTimeout1');
}, 0);
requestAnimationFrame(() => {
  console.log("requestAnimationFrame");
});
setTimeout(() => {
  console.log('setTimeout2');
}, 0);

绝大多数情况是

少数情况会出现这样的结果

极少数情况下会出现

相关推荐
Emily156853598703 分钟前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
To_OC5 小时前
面试被问了三回三栏布局,这次我终于把 BFC 那层窗户纸捅破了
前端·css·面试
城管不管5 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅5 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
To_OC5 小时前
啃完 TS 工具类型我发现:Pick 和 Omit 原来就是一层窗户纸
前端·面试·typescript
风月说与山鬼7 小时前
三、大括号语法
前端·react.js
kyriewen7 小时前
我把最常踩的8个CORS跨域报错整理了一遍——第8个去年还不存在
前端·javascript
Csvn8 小时前
🕰️ 闭包 + setTimeout 的 5 个经典陷阱:为什么定时器看到的永远不是最新的值?
前端
lilian2338 小时前
Harmony os 技术实战|拼豆制图27:用单字符编码承载 50 张 70×70 图纸
前端·数据库·华为·harmonyos