事件循环
事件循环是单线程JavaScript为了处理异步操作中执行顺序的问题,通过监听task队列和microtask 队列实现异步操作
Event Loop 通过监听不同队列,当队列中有任务的时候取出任务并进行执行,执行完成后检查是否还有任务,如果哟继续执行,直到为空为止。
最新的W3C准则将任务队列分为微任务队列和任务队列
微任务队列(microtask queue)
先进先出,事件循环中只会拥有一个
任务队列
普通任务分为五个可具有优先级的队列
| 队列类别(规范术语) | 常见任务源 / API | 优先级说明 |
|---|---|---|
| 交互队列(user interaction task queue) | click、keydown、pointerup 等 UI 事件 | 最高,浏览器必须优先处理 |
| 渲染队列(rendering task queue) | requestAnimationFrame、插值动画、ResizeObserver | 次高,每轮循环最多运行一次 |
| 高优先级调度队列(scheduler user-blocking queue) | scheduler.postTask(cb, {priority:'user-blocking'}) | 高,由 Prioritized Task Scheduling API 产生 |
| 普通/延时队列(generic / timer task queue) | setTimeout、setInterval、MessageChannel | 默认优先级 |
| 后台队列(scheduler background queue) | scheduler.postTask(cb, {priority:'background'}) | 最低,浏览器空闲时才选 |
循环过程是通过从高到低五个队列查找第一个非空队列,使用FIFO策略进行获取任务
执行顺序
html中遇到
– 同步脚本:解析器碰到就立即开始; – defer 脚本:DOM 解析完后、渲染前一次性开始; – async 脚本:下载完后作为一次普通 task 被事件循环挑中才开始。
同步脚本中如果网络请求慢了会一直等待脚本的加载
- 执行同步代码
- 当执行栈为空,查询microtask 队列,如有执行队头任务
- 查询任务队列
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
console.log("script start1");
Promise.resolve().then(() => {
console.log("promise1");
});
setTimeout(() => {
console.log("setTimeout1");
}, 0);
scheduler.postTask(
() => {
console.log("postTask1");
},
{ priority: "user-blocking" }
);
/*
script start1
promise1
postTask1
setTimeout1
*/
</script>
</body>
</html>