事件循环讲解

事件循环是什么

事件循环是 JS 安排任务的一套规则。

因为 JS 是单线程的,一次只能做一件事,遇到耗时的操作(比如网络请求、定时器),不能卡住等着,得先干别的,等耗时操作完了再回来处理。

任务分两种

JS 把任务分成两种,优先级不一样:

  • 宏任务:setTimeout、setInterval、DOM 事件、Ajax 请求
  • 微任务:Promise.then、async/await 后面的代码

微任务的优先级永远高于宏任务。

具体执行顺序是:

  1. 先把手头的同步代码全部执行完
  2. 把队列里所有微任务一次性清空
  3. 从宏任务队列里取出一个执行
  4. 执行完这个宏任务后,再去看微任务队列有没有新的,有就清空
  5. 再取下一个宏任务...循环往复

几个例子=v=

例 1

javascript 复制代码
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);

输出:1 4 3 2

解释:1 和 4 是同步代码,直接输出。Promise.then 是微任务,排在同步后面先执行,所以 3 先于 2 输出。setTimeout 是宏任务,即使延迟是 0 秒,也得等所有微任务清空才轮到它。

例 2

javascript 复制代码
setTimeout(() => {
  console.log('A');
  Promise.resolve().then(() => console.log('B'));
}, 0);

setTimeout(() => console.log('C'), 0);

输出:A B C

A 是第一个宏任务,输出后产生了微任务 B,在当前宏任务结束后立刻清空微任务,然后才取下一个宏任务 C。

例 3

javascript 复制代码
Promise.resolve().then(() => {
  console.log(1);
  setTimeout(() => console.log(2), 0);
});

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

输出:1 3 2

微任务先执行,输出 1,同时把 2 放进了宏任务队列。然后取宏任务,队列里现在有 3 和 2(按入队顺序),先取 3,再取 2。

例 4

javascript 复制代码
async function test() {
  console.log('a');
  await console.log('b');
  console.log('c');
}
test();
console.log('d');

输出:a b d c

执行 test() 时,先输出 a,然后 await console.log('b') 会先执行 b,但 await 后面的 c 会被当作微任务挂起,继续执行外面的同步代码 d,最后微任务执行输出 c。


常问考点

  1. setTimeout 0 秒不是真的 0 秒,浏览器至少会延迟 4ms,而且还要等主线程空闲和微任务清空。
  2. 微任务里产生的新微任务,会在本轮一并清空,不会留到下一轮宏任务。
  3. 宏任务队列是有顺序的,谁先被放进去谁先被执行。
  4. Promise 的状态变化和 then 的执行时机:then 里的回调是微任务,但前提是 Promise 状态已经变了。如果 Promise 一直 pending,then 永远不会执行。
  5. await 的本质:await 后面的代码相当于被包了一层 Promise.then,所以是微任务。

为什么有时候会搞混

最大的误解来源是:以为 setTimeout 的延迟时间决定执行顺序。

实际上,延迟时间只决定了这个任务什么时候被放进宏任务队列,而不是什么时候被执行。它还要等当前所有同步代码和微任务都处理完,才轮得到它。

另一个容易混淆的点是:宏任务和微任务不在同一个队列里,微任务队列清空后才会去看宏任务队列。所以即使微任务是在宏任务执行过程中产生的,也会在当前宏任务结束后立刻处理,而不是排在下一个宏任务后面。


判断技巧

看到一道输出顺序题,按这个顺序推:

  1. 先找所有同步代码,它们最先执行
  2. 再找微任务(Promise.then、await 后面的代码),它们会在同步代码之后、宏任务之前执行
  3. 最后找宏任务(setTimeout、setInterval),它们最后执行,而且是按入队顺序一个一个来,每执行完一个宏任务就清空一次微任务队列
相关推荐
我的div丢了肿么办30 分钟前
element-plus表格使用:filter-method过滤导致出现无数据
前端·javascript·vue.js
YIAN40 分钟前
入门篇:LangGraph 入门实战:从零搭建最简单的 RAG 问答系统
javascript·langchain
YIAN1 小时前
高级篇:进阶多跳 RAG:子问题拆解 + 循环检索 + 智能规划,搞定复杂链式问题
javascript·langchain
YIAN1 小时前
前置篇:手把手用 Milvus + LangChain 搭建小说向量知识库,《天龙八部》EPUB 一键入库
javascript·langchain
YIAN1 小时前
进阶篇:RAG 优化第一步:给你的问答系统加上智能路由,实现双分支分流
javascript·langchain
inksci1 小时前
发布Ai生成的html markdown 推荐
前端·javascript·html
是江迪呀2 小时前
向全世界售卖你的产品:做了一款教程引导插件
前端·javascript·后端
码艺-Alimjan3 小时前
维吾尔语 RTL 排版的隐形利器:`\u200F`
前端·javascript
Highcharts.js4 小时前
Highcharts 数据过滤实战指南:从基础实现到性能优化
javascript·性能优化·bi·highcharts·前端可视化·数据过滤·交互图表
何何____5 小时前
TypeScript 函数类型兼容性
前端·javascript·typescript