javascript无法直接用performance api测量事件循环耗时,因其仅提供挂钟时间;但可通过mark/measure结合微宏任务时机间接观测排队延迟,辅以long tasks api捕获≥50ms长任务来判断卡顿。

JavaScript 中不能直接用 performance API 测量事件循环本身的耗时,因为 performance 提供的是高精度时间戳(如 performance.now()),它记录的是**挂钟时间(wall-clock time)**,而非事件循环各阶段的执行时长。但你可以结合 performance.mark()、performance.measure() 和一些关键时机(比如 setTimeout(fn, 0)、Promise.then、queueMicrotask)来间接观测任务排队与执行的延迟,从而推断事件循环的“繁忙程度”或微任务/宏任务的排队耗时。
用 performance.mark 捕捉关键时间点
在可能受事件循环阻塞影响的位置打标记,比如:
- 同步代码开始前 / 后
- 微任务回调中(
Promise.then或queueMicrotask) - 宏任务回调中(
setTimeout(() => {}, 0))
例如:
performance.mark('start-sync');
// 模拟一段耗时同步操作(如大数组排序)
const arr = new Array(1e6).fill(0).map((_, i) => i);
arr.sort(() => Math.random() - 0.5);
performance.mark('end-sync');
<p>queueMicrotask(() => {<br>
performance.mark('microtask-start');<br>
}); </p><p>setTimeout(() => {<br>
performance.mark('macrotask-start');<br>
}, 0); </p><p>// 后续可 measure 它们之间的间隔<br>
performance.measure('sync-duration', 'start-sync', 'end-sync');<br>
performance.measure('sync-to-microtask', 'end-sync', 'microtask-start');<br>
performance.measure('sync-to-macrotask', 'end-sync', 'macrotask-start');</p>观测微任务队列延迟(microtask latency)
微任务(如 Promise.then)会在当前任务结束后立即执行,但如果主线程被长时间同步代码阻塞,它的实际执行时间就会延后。这个延后时间就是微任务延迟,能反映事件循环“上一轮”是否积压了大量工作:
- 在关键操作前调用
queueMicrotask(() => performance.mark('mt-queued')) - 在微任务回调里再打一个标记:
performance.mark('mt-executed') - 用
performance.measure('mt-latency', 'mt-queued', 'mt-executed')得到排队+执行耗时(通常执行本身极短,主要看排队)
用 longtask API 辅助判断事件循环卡顿
现代浏览器支持 Long Tasks API,它由浏览器主动上报持续 ≥ 50ms 的任务(即可能造成掉帧的长任务)。这不是 performance 的子模块,但和性能观测强相关:
- 通过
performance.getEntriesByType('longtask')获取历史长任务 - 配合
PerformanceObserver实时监听:
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Long task:', entry.startTime, entry.duration);
}
}).observe({ entryTypes: ['longtask'] });这比手动打点更可靠——它由引擎底层触发,不受 JS 执行顺序干扰,是观测事件循环是否被阻塞的最直接指标。
注意:performance.now 不等于 event loop tick 时间
performance.now() 返回的是从页面加载开始的毫秒数(精确到千分之一毫秒),但它不告诉你当前处于事件循环哪个阶段(macrotask queue?microtask queue?渲染?空闲?)。你无法靠它“测出”一次 tick 花了多久。真正有意义的是:
- 两个标记之间的时间差(如从同步结束到微任务开始)→ 反映 microtask 队列等待时长
- 连续两次
setTimeout(fn, 0)的间隔是否明显大于 4ms(浏览器最小间隔)→ 推测是否被其他任务挤压 - 结合
requestIdleCallback的deadline.timeRemaining()→ 判断当前空闲时间是否充足
这些组合才能逼近对事件循环健康度的判断。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











