事件循环 Event Loop
浏览器为了协调同步和异步任务制定的一种工作机制,主线程先依次执行同步任务,同步任务中有一些异步回调,将其推入到任务队列中
宏任务与微任务
宏任务 Macro-Task
宿主环境(运行 JS 的环境,例如浏览器)提供的叫宏任务,主要包括创建⽂档对象、解析HTML、执⾏主线JS代码以及各种事件如⻚⾯加载、输⼊、⽹络事件和定时器等
微任务 Micro-Task
语言标准(例如ECMA)提供的叫微任务,微任务的例⼦有 Promise 回调函数、DOM变化等。
执行顺序为:一个宏任务 =》所有微任务 =》下一个宏任务,可以点此体验一下
requestAnimationFrame
window.requestAnimationFrame()
该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行,也属于异步执行的方法,但是该方法既不是宏任务,也不是微任务
事件循环的过程
- 执行完同步任务
- 检查
microtask queues
微任务队列是否为空,非空则到第 3 步,为空到第 4 步 - 执行
microtask queues
微任务队列中的所有任务 - 检查
macrotask queues
宏任务队列是否为空,非空则到第 5 步,为空到第 6 步 - 取出
macrotask queues
中的一个任务,在执行栈执行,执行完成后返回到第 2 步 - 执行视图更新
以 Promise 与 setTimeout 举例,执行以下代码:
js
Promise.resolve().then(() => {
console.log('Promise1');
setTimeout(() => {
console.log('setTimeout2');
}, 0);
});
setTimeout(() => {
console.log('setTimeout1');
Promise.resolve().then(() => {
console.log('Promise2');
});
}, 0);
上述代码的输出结果为:Promise1 -> setTimeout1 -> Promise2 -> setTimeout2
,其事件循环过程如下所示:
- 一开始执行栈的同步任务执行完毕,检查微任务队列
- 清空微任务队列 ,此时输出
Promise1
,并将setTimeout2
放入宏任务队列,此时宏任务队列中任务是 [setTimeout1
,setTimeout2
] - 从宏任务队列中取出第一个任务执行,输出
setTimeout1
,并将Promise2
放入微任务队列,此时微任务队列中任务是 [Promise2
] - 接着又检查并清空微任务队列,输出
Promise2
- 再次清空
microtask queues
后查询宏任务队列[setTimeout2
],取出第一个任务执行,,输出setTimeout2