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

Scheduler.postTask:给宏任务装上「优先级」

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

1 · 三档优先级

2 · 乱序排入,按优先级执行

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

规则:跨优先级看挡位 (user-blocking → user-visible → background),同优先级内 FIFO。所以「响应输入」总能插到「上报埋点」前面,哪怕埋点的代码写在更前面。这正是 setTimeout 给不了的。

3 · 还能取消 (TaskController)

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

4 · 和老办法对比

setTimeout(fn,0) requestIdleCallback scheduler.postTask
优先级 只有「空闲」一档 三档,可动态改
可取消 clearTimeout cancelIdleCallback AbortController 统一
最小延时 ≥4ms (嵌套) 不定 无人为下限
后台限流 ≥1s
返回值 定时器 id 回调 id Promise (能 await)
浏览器支持 全部 除 Safari 较新,需 polyfill

配套 API:scheduler.yield()。在一个长任务中途 await scheduler.yield(),会让出主线程给更紧急的输入 / 渲染,随后仍按原优先级继续执行——优于手动 await new Promise(r => setTimeout(r))(yield 返回后优先于新排入的同类任务,不会被挤到队尾)。时间切片的现代写法依赖于它,应用页将继续讨论长任务与 INP。