You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于ECMAScript规范中Job与Job Queue的技术疑问

我来逐个解答你关于ECMA-262第8版(ES2017)和浏览器事件循环的问题:

1. ScriptJobs与PromiseJobs的优先级关系

在ECMA-262第8版的定义里,PromiseJobs属于微任务队列,而ScriptJobs是承载脚本执行任务的队列(属于宏任务范畴)。虽然ECMA规范本身的RunJobs抽象操作没有强制规定严格的优先级顺序,但在浏览器这种实际运行环境中,结合HTML规范的事件循环机制,PromiseJobs的优先级是明确高于ScriptJobs的:

  • 每当执行完一个同步任务栈(或者一个Job任务)后,浏览器会先把PromiseJobs队列里的所有任务清空,才会去处理ScriptJobs这类宏任务队列的任务。
2. RunJobs抽象操作的执行时机与触发场景

RunJobs是ECMA规范定义的一个“后台”抽象操作,它的核心作用就是持续检查并执行所有Job队列里的任务,直到所有队列都为空。它的触发时机和场景主要包括:

  • 全局脚本(比如页面里的<script>标签)执行完毕后,会启动RunJobs来处理已经排队的任务;
  • 当Promise的状态变更(比如resolve/reject被调用)时,对应的回调会被加入PromiseJobs队列,此时会触发RunJobs来处理这些微任务;
  • 每执行完一个Job任务(比如动态插入的脚本执行完成)后,都会再次触发RunJobs,检查是否还有剩余的任务需要处理。
3. Firefox 60中动态脚本的执行时机与日志解析

先看你的代码运行日志:script start → 3 → script end → 2 → 1,我们一步步拆解原因:

为什么动态创建的<script>会在当前上下文执行?

这是HTML规范明确规定的行为:当你通过document.appendChild()这类方法把动态创建的<script>元素插入DOM时,浏览器会立即同步执行这个脚本,而不是把它放到宏任务队列里等待。所以在你调用document.body.appendChild(x)的瞬间,脚本里的console.log(3)就直接执行了,自然插在script start和script end之间。

完整日志顺序解析

  1. 首先执行console.log('script start'),输出script start;
  2. 创建并插入<script>元素,浏览器立即执行脚本内容,输出3;
  3. 回到当前回调函数,执行console.log('script is end'),输出script end;
  4. 当前DOMContentLoaded回调的同步代码执行完毕,执行栈清空,此时开始处理微任务队列:Promise.resolve(2).then(console.log)的回调已经在PromiseJobs里了,执行它输出2;
  5. 微任务队列清空后,才会处理宏任务队列:setTimeout的回调是宏任务,执行它输出1。

补充:HTML规范对Job队列选择的定义

ECMA-262里的RunJobs提到“以实现定义的方式选择非空Job队列”,但HTML规范给浏览器的事件循环做了明确的任务处理顺序规则:

  1. 先执行当前执行栈中的所有同步代码;
  2. 执行栈清空后,优先处理所有微任务队列(包括PromiseJobs)的任务,直到微任务队列为空;
  3. 然后从宏任务队列(包括ScriptJobs、setTimeout、DOM事件回调等)中取出一个任务执行;
  4. 重复上述循环。

简单说就是:微任务永远比宏任务先执行,PromiseJobs属于微任务,ScriptJobs属于宏任务,所以优先级差异很明确。

关于你给出的Job队列执行逻辑的验证

你写的这段逻辑是完全正确的:

ScriptJobs.push(something); // pop and run by Event loop
// blow will happens while `something` runs
PromiseJobs.push(anotherOne);
ScriptJobs.push(theother);
//end of `something`
//and in here, PromiseJobs will pop?

当something这个ScriptJobs任务执行时,中途把anotherOne加入PromiseJobs、theother加入ScriptJobs:

  • 等something执行完毕,当前执行栈清空,浏览器会先处理所有微任务,也就是PromiseJobs里的anotherOne会被取出执行;
  • 只有当微任务队列彻底清空后,才会回到宏任务队列,取出theother这个ScriptJobs任务执行。

内容的提问来源于stack exchange,提问作者ENvironmentSet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:11:47