JS - 从规范解读浏览器事件循环

本文深入解析浏览器事件循环模型,按照规范解读 Jobs 的执行标准,探讨宏任务(MacroTask)与微任务(MicroTask)的执行顺序,包括Promise-handling Jobs和其他Jobs的处理。通过代码分析,展示JavaScript中setTimeout、Promise等如何影响事件循环。
摘要由CSDN通过智能技术生成

阅读本文请先查看执行上下文

首先, 介绍几个规范定义的概念:

  • host
    规范定义的比较抽象, 可简单理解为浏览器或Node这种宿主环境
  • job
    规范定义的比较抽象, 简单来说就是 host 的钩子 参考附录D
    对于浏览器, setTimeout , requestAnimationFrame , Promise.then 等回调函数就是钩子

Jobs 执行规范

  • 在某个时间点, 没有运行中的执行上下文且执行上下文栈为空, 那么执行:
  1. 执行 host 定义的准备操作
  2. 运行 job
  3. 执行 host 定义的清理操作, 在此之后, 执行上下文栈必须为空
  • 在任何一个时间点, 至多执行一个 job
  • 一个 job 开始后, 在其它 job 开始前必须执行完成
  • job必须返回 NormalCompletion, 并且实现异常处理

job 的顺序由 host 决定, 对于浏览器环境, Promise-handling Jobs (通常为Promise) 具有更高优先级

判断代码准备好执行的标准

  • 执行上下文栈不为空
    即待运行代码的执行上下文初始化完成
  • 执行上下文栈最上面的执行上下文的 Realm 必须是 Realm Record
    Realm 初始化完成

浏览器事件循环模型

执行上下文栈遵循 LIFO(后进先出)

任务队列遵循 FIFO(先进先出)

普遍把浏览器任务队列分为:

  • MacroTask(宏任务)
    主要包括 setTimeout , setInterval , requestAnimationFrame

  • MicroTask(微任务)
    主要包括 Promise

本文从规范解读, 按规范分为 Promise-handling Jobs 以及其它 Jobs

在这里插入图片描述

代码分析

只分析执行顺序, 不详细介绍技术细节

function f1() {
    console.log("f1");
}

function f2() {
    console.log("f2");
}

function f3() {
    console.log("f3");
}

function f4() {
    const s = new Date().getSeconds();

    while (true) {
        if (new Date().getSeconds() - s >= 2) {
            console.log("looped for 2 seconds");
            break;
        }
    }
}

setTimeout(f1, 0);

Promise.resolve().then(f2).then(f3);

new Promise((resolve) => {
    console.log("resolve");
    resolve();
}).then(f4);

part1

创建执行上下文

执行上下文入栈

执行代码

执行 setTimeout(f1, 0) , 设置一个定时器, 当定时器触发时(事实上, 定时有一个最小值, 所以设置时间为 0 的时候, 会以定时最小值触发), 会把回调函数 f1 推入 Jobs 队列

执行 Promise.resolve() , 返回状态为 fulfilledPromise , 执行 .then(f2) , 把回调函数 f2 推入 Promise-handling Jobs

执行 new Promise(f) , f 在创建 Promise 实例时被执行, console.log("resolve") => 打印 resolve, resolve() => Promise 实例状态被置为 fulfilled , Promise 实例执行 .then(f4) , 把回调函数 f4 推入 Promise-handling Jobs

执行结束

执行上下文出栈

注意: 基本上, 一个函数执行前, 会创建相应的执行上下文. 执行 new Promise(f) , Promise.resolve() , setTimeout(f1, 0) 这些函数实际上都创建了相应的执行上下文用于执行代码, 这里未做详细讨论.

在这里插入图片描述

part2

当前满足 Jobs 执行规范, Promise-handling Jobs 具有高优先级, 检查不为空, 开始任务 f2

创建 f2 执行上下文

执行上下文入栈

执行代码

执行 console.log("f2") , 打印 f2

执行结束

注意 .then(f2) 返回了一个状态为 fulfilledPromise , 执行 .then(f3) , 把回调函数 f3 推入 Promise-handling Jobs

执行上下文出栈

在这里插入图片描述

part3

当前满足 Jobs 执行规范, Promise-handling Jobs 具有高优先级, 检查不为空, 开始任务 f4

创建 f4 执行上下文

执行上下文入栈

执行代码

执行 while , 等待 2s 打印 looped for 2 seconds, 注意, 通过 setTimeout(f1, 0) 创建的定时器在最短时间被触发, f1 推入 Jobs

执行结束

执行上下文出栈

在这里插入图片描述

part4

当前满足 Jobs 执行规范, Promise-handling Jobs 具有高优先级, 检查不为空, 开始任务 f3

参考 part2, 打印 f3

part5

当前满足 Jobs 执行规范, Promise-handling Jobs 具有高优先级, 检查为空, 检查 Jobs, 检查不为空, 开始任务 f1

参考 part2, 打印 f1

end

所以最终打印:

resolve
f2
looped for 2 seconds
f3
f1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值