Web 平台 API / 事件循环 · 任务、microtask,与那一圈的所有时机 / Scheduler.postTask:给 macrotask 装上「优先级」 待审核 5 / 6
scheduler.postTask · 带优先级的 macrotask

Scheduler.postTask:给 macrotask 装上「优先级」

setTimeout 的局限是没有优先级:一个上报埋点和一个响应用户点击,排入队列后同等对待,先到先执行。scheduler.postTask() 解决了这一点——每个任务带一个优先级user-blocking / user-visible / background),浏览器按优先级调度,还能中途调整优先级、取消,并与输入 / 渲染统一排程。这是 W3C Scheduling APIs 的一部分,可理解为「带优先级的 setTimeout」。

1 · 三档优先级

图 1-1 · 三档优先级各自的排队与执行时机。

2 · 乱序排入后的执行顺序

下面按源码顺序把 6 个任务一次性 postTask 排入(优先级混合),观察它们实际的执行顺序:

图 2-1 · 六个混合优先级的任务按源码顺序排入后的实际执行顺序。

注 · 跨优先级看挡位(user-blockinguser-visiblebackground),同优先级内 FIFO。实测 Chrome 151:按 backgrounduser-blockinguser-visibleuser-blocking 的源码顺序排入,执行顺序为两个 user-blockinguser-visiblebackground(核对于 2026-08)。所以「响应输入」总能插到「上报埋点」前面,哪怕埋点的代码写在更前面。这正是 setTimeout 给不了的。

3 · 还能取消 (TaskController)

postTask 配一个 TaskController:把它的 signal 传进去,任务跑之前controller.abort() 就能撤销(返回的 promise 以 AbortError 拒绝)。同一个 controller 还能 setPriority() 动态调档

图 3-1 · TaskController 的取消与动态调档:任务执行前 abort 使返回的 promise 以 AbortError 拒绝。

4 · 和老办法对比

浏览器支持一行核对于 2026-08,数据取自 BCD 8.0.11。
setTimeout(fn,0) requestIdleCallback scheduler.postTask
优先级 只有「空闲」一档 三档,可动态改
可取消 clearTimeout cancelIdleCallback AbortController 统一
最小延时 ≥4ms (嵌套) 不定 无人为下限
后台限流 1s,隐藏久了降到 1min 规范未规定
返回值 定时器 id 回调 id Promise (能 await)
浏览器支持 全部 Chrome 47 / Firefox 55,Safari 未实现 Chrome 94 / Firefox 142,Safari 未实现

注 · 配套 API scheduler.yield()在一个长任务中途 await scheduler.yield(),会让出主线程给更紧急的输入与渲染,随后仍按原优先级继续执行——优于手动 await new Promise(r => setTimeout(r)):延续任务排在同优先级的新任务之前,不会被挤到队尾。实测 Chrome 151,yield 之后的代码先于让出期间新 postTask 的同档任务执行(核对于 2026-08)。scheduler.yield 需 Chrome 129 或 Firefox 142。时间切片的现代写法依赖于它,应用页将继续讨论长任务与 INP。