为什么JavaScript事件循环未设计微任务超时中断机制?
为什么事件循环没有为微任务设置超时机制?
首先看一段典型的微任务永久阻塞代码:
async function() { while (true) { await null; } }
上述代码会永久阻塞线程,本质原因是事件循环的规则要求单帧内必须清空所有微任务队列,才会进入后续渲染、宏任务处理阶段。
再看另一个常见的长微任务场景:
(async () => { for (let i = 0; i < 999999; i++) { await null; document.body.appendChild(document.createElement("span")); console.log("running") } console.log("finished") })();
如果给微任务处理增加超时限制,运行逻辑会变为:
- 事件循环处理微任务满1秒后,暂停执行下一个待处理微任务
- 引擎让出线程处理其他优先级任务,例如动画帧回调、用户输入响应、页面渲染
- 进入下一帧后,事件循环从上次暂停位置继续处理剩余微任务
在该设计下,上述while (true)循环每帧仅会阻塞线程1秒,因为await null;属于可挂起操作,不会出现永久卡死的情况。
基于生成器的超时调度Polyfill实现
可以使用yield模拟await行为,实现上述带超时的微任务调度逻辑:
const tasks = new Array; const resolvedValues = new Array; const runnables = new WeakSet; function process(task, resolved) { try { const result = task.next(resolved); if (result.done) { return null; } const { value } = result; if (value === undefined) { tasks.unshift(task); resolvedValues.unshift(undefined); return; } Promise.resolve(value).then((value) => { tasks.unshift(task); resolvedValues.unshift(value); if (unsubscribed) { unsubscribed = false; requestAnimationFrame(listener) } }) } catch (e) { console.error(e) } return null; } let unsubscribed = true; const listener = function() { const ms = Date.now(); while (Date.now() - ms < 100) { if (process(tasks.pop(), resolvedValues.pop()) === null) { break; } } if (tasks.length > 0) { requestAnimationFrame(listener); return; } unsubscribed = true }; function suspend(runnable) { const task = runnable(); if (runnables.has(task)) { return; } runnables.add(task); if (process(task) === null) { return; } if (unsubscribed) { unsubscribed = false; requestAnimationFrame(listener) } } export { suspend };
无限循环场景测试用例:
let n = 0; suspend(function*() { while (true) { yield; n++; console.log("running") } }); const resetCount = () => { console.log(n); n = 0; requestAnimationFrame(resetCount); }; requestAnimationFrame(resetCount);
长列表DOM操作场景测试用例:
suspend(function*() { for (let i = 0; i < 999999; i++) { yield; document.body.appendChild(document.createElement("span")); console.log("running") } console.log("finished") });
核心疑问
有观点提出“这种设计可能会破坏现有逻辑”,我认可该判断,但想明确具体会破坏哪些场景?
注:我并非主张必须落地该特性,只是希望了解:在微任务、Promise诞生前,不存在向后兼容约束的阶段,事件循环为何没有采用这种设计?
内容的提问来源于stack exchange,提问作者Worldwidebrine
相关产品推荐
相关产品推荐

