浏览器事件循环(Event Loop)

📅 2026/7/11 3:35:52 👁️ 阅读次数 📝 编程学习
浏览器事件循环(Event Loop)

彻底搞懂!

有多少种任务:同步任务,微任务,宏任务

在 JavaScript 引擎(V8)的字典里,根本没有“异步任务”这个词,只有“同步任务”和“回调函数”。

同步任务属于宏任务,但宏任务不一定是同步任务。所有的同步代码,其实都是第一个默认的“宏任务”。

真正的微任务只有这几个:Promise.then/catch/finally、MutationObserver、queueMicrotask。

常见的宏任务:setTimeout、setInterval、I/O操作、UI 渲染、全局 script 代码。普通的异步请求(如 fetch / ajax)虽然它们是异步的,但它们属于 I/O 操作,执行完后的回调会被放入宏任务队列中等待执行。

有多少种任务队列:微任务队列,宏任务队列

类比:厨师为js引擎(单线程)即一整个js代码片段,厨师要做什么菜是同步任务,客人又点了可乐但是厨师在炒饭,所以厨师直接给了服务员(webapis)去做可乐(“异步任务”-区分同步任务的说法,这里的可乐我们给他设定为promise.then),在做完菜之后,微任务有插队机制,所以厨师需要先给可乐,结束所有微任务之后再给炒饭,之后厨师就回去做下一道菜,从宏任务里面取出同步任务继续循环。

【执行一个宏任务】 ➡️ 【清空所有微任务】 ➡️ 【执行下一个宏任务】 ➡️ 【清空所有微任务】...(无限循环)

console.log('1. 同步代码'); setTimeout(() => { console.log('2. setTimeout 宏任务'); // 注意:在宏任务内部又产生了微任务 Promise.resolve().then(() => { console.log('7. 第一个 setTimeout 里的微任务'); }); }, 0); setTimeout(() => { console.log('8. 第二个 setTimeout 宏任务'); }, 0); Promise.resolve().then(() => { console.log('3. Promise 微任务'); }); async function asyncFn() { console.log('4. async 函数内的同步代码'); await Promise.resolve(); console.log('5. async 函数内 await 后的代码'); } asyncFn(); console.log('6. 同步代码结束');

✅ 正确的打印顺序是:1 -> 4 -> 6 -> 3 -> 5 -> 2 -> 7 -> 8

阅读整个js片段,js引擎开始执行,同步代码直接输出,将微任务放入微任务队列,宏任务放入宏任务队列

第一轮:

console输出1

第一个settimeout的回调进入宏任务,不管

第二个settimeout的回调进入宏任务,不管

promise的回调,进入微任务,不管

调用函数asyncFn() -> 进入内部 -> console输出4 -> await 关键字后面的代码,相当于被放进了 Promise.then 中,所以直接进入微任务,不管

console输出6

结果:1 4 6

第二轮:

检查微任务队列

promise的回调,console输出3

await 关键字后面的代码,console输出5

结果:1 4 6 3 5

第三轮:

检查宏任务队列

第一个settimeout,console输出2,后面跟了一个promise的回调,进入微任务,不管

这个时候当前宏任务结束,在下一个宏任务开始之前,检查微任务队列

结果:1 4 6 3 5 2

第四轮:

检查微任务队列

promise的回调,console输出7,微任务结束

结果: 1 4 6 3 5 2 7

第五轮:

检查宏任务队列

第二个settimeout,console输出8

结果:1 4 6 3 5 2 7 8