浏览器事件循环全解析:宏任务微任务如何工作的(宏任务微任务执行顺序)?
用一张运行时序图 + 一段代码,彻底说清宏任务、微任务谁先谁后。读完能秒答面试“输出顺序”题,也能自己预测任何 Promise / setTimeout / async-await 代码的执行结果。
·
1. 如何工作的?
事件循环又叫做消息循环,是浏览器主线程的工作方式,JS 是浏览器的语言。因为 JS 是单线程,这意味着它一次只能做一件事,为了解决代码阻塞的问题,出现了任务循环。每次循环去任务队列取任务到主线程进行执行。
- 执行同步代码:首先,整段 <script> 代码被当作第一个宏任务来执行。遇到同步代码,立即执行。
- 分发异步任务:在执行过程中,如果遇到微任务(如 Promise.then()),就将其放入微任务队列;如果遇到新的宏任务(如 setTimeout),就将其放入宏任务队列。
- 清空微任务:当第一个宏任务(也就是初始的同步代码)执行完毕后,主线程会立即检查微任务队列,然后执行并清空所有的微任务。
- UI 渲染(浏览器环境):在微任务队列清空后,浏览器可能会进行一次页面渲染。
- 执行下一个宏任务:然后,事件循环会去宏任务队列中,只取出一个任务来执行。
重复循环:这个新的宏任务执行完毕后,再次重复第 3 步:清空所有微任务 -> (可能)UI渲染 -> 再去取下一个宏任务... 这个过程不断循环。
2. 举个例子
console.log('Start'); // 宏任务 (script)
setTimeout(() => {
console.log('setTimeout 1'); // 宏任务
Promise.resolve().then(() => {
console.log('Promise inside setTimeout'); // 微任务
});
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1'); // 微任务
});
setTimeout(() => {
console.log('setTimeout 2'); // 宏任务
}, 0);
console.log('End'); // 宏任务 (script)
执行顺序分析:
第一次宏任务 (script):
- console.log('Start'); 输出 Start
- setTimeout 被添加到宏任务队列。
- Promise.resolve().then() 被添加到微任务队列。
- setTimeout 被添加到宏任务队列。
- console.log('End'); 输出 End
清空微任务队列:
- 执行 Promise.resolve().then(),输出 Promise 1
第一次宏任务 (setTimeout 1):
- 执行第一个 setTimeout 的回调,输出 setTimeout 1
- Promise.resolve().then() 被添加到微任务队列。
清空微任务队列:
- 执行 Promise inside setTimeout,输出 Promise inside setTimeout
第二次宏任务 (setTimeout 2):
- 执行第二个 setTimeout 的回调,输出 setTimeout 2
Start
End
Promise 1
setTimeout 1
Promise inside setTimeout
setTimeout 2
3. 为什么会有宏任务微任务之分?
其实是为了优先级考虑,如果说正在执行一个耗时较长的宏任务,突然来了一个 API 请求,那么这个这个数据可能要等很久才能显示,体验不是很好。
所以说有宏任务和微任务之分,是让紧急任务可以插队,在当前任务完成后立刻执行,应用响应更快。
更多推荐
所有评论(0)