why is the setInterval task executed slower than the setTimeout task in the browser javascript environment?
why is the setInterval
task executed slower than the setTimeout
task in the browser javascript environment?
为什么在浏览器 javascript 环境下 setInterval 任务执行速度比 setTimeout 任务慢?
setTimeout(() => {
console.log(`4`);
});
let id = setInterval(() => {
console.log(`5`);
clearInterval(id);
});
Promise.resolve().then(() => console.log(`2`));
queueMicrotask(() => {
console.log(`3`);
});
console.log(`1`);
/*
1
2
3
4
5
*/
Chrome bug ❓
https://bugs.chromium.org/p/chromium/issues/detail?id=1421612
??? Event Loop task priority / 事件循环任务优先级
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#stack
??? js event loop timers proority
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/
https://stackoverflow.com/questions/19743354/does-javascript-event-queue-have-priority
源码
https://github.com/search?q=repo%3Av8/v8 setInterval&type=code
which includes eval(), Function(), setTimeout() and setInterval()
https://github.com/v8/v8/blob/4f632833589b37bc4c71d36180d0a31caaee9ad5/include/js_protocol.pdl#L1491
https://github.com/search?q=repo%3Av8/v8 setTimeout&type=code
https://github.com/v8/v8/blob/4f632833589b37bc4c71d36180d0a31caaee9ad5/include/v8-microtask-queue.h
https://github.com/v8/v8/blob/4f632833589b37bc4c71d36180d0a31caaee9ad5/include/v8-microtask.h
源码解析
https://github.com/learning-js-by-reading-source-codes/v8
setTimeout
定时器时间不准确
bug ❌
let startTime = new Date();
setTimeout(() => {
let endTime = new Date();
console.log(endTime - startTime);
}, 0);
for (let i = 0; i < 10**6; i++) {
// 任务中的同步代码执行时间过长,导致异步宏任务 setTimeout 的执行时间延后 bug
}
Firefox enforces additional throttling for scripts that it recognizes as tracking scripts.
When running in the foreground, the throttling minimum delay is still 4ms.
In background tabs, however, the throttling minimum delay is 10,000 ms, or 10 seconds, which comes into effect 30 seconds after a document has first loaded.
Firefox 对其识别为跟踪脚本的脚本实施额外的限制。
在前台运行时,throttling 最小延迟
仍然是 4ms
。
但是,在后台选项卡
中,最小延迟限制为 10,000 毫秒
或 10 秒
,它在文档首次加载 30 秒后
生效。
https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#throttling_of_tracking_scripts
Maximum delay value Browsers store the delay as a 32-bit signed integer internally. This causes an integer overflow when using delays larger than 2,147,483,647 ms (about 24.8 days), resulting in the timeout being executed immediately.
最大延迟值
浏览器在内部将延迟存储为 32 位有符号整数
。当使用大于 2,147,483,647
毫秒(约 24.8 天
)的延迟时,这会导致整数溢出
,导致立即执行
超时。
https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value
setInterval
delay bug ❌
// 高精度时间戳
https://developer.mozilla.org/en-US/docs/Web/API/setInterval#delay_restrictions
HTML Standard
Timers can be nested;
after five such nested timers, however, the interval is forced to be at least four milliseconds.
定时器
可以嵌套;
然而,在五个这样的嵌套定时器之后,间隔被强制至少为四毫秒。
This API does not guarantee that timers will run exactly on schedule.
Delays due to CPU load, other tasks, etc, are to be expected.
此 API 不保证计时器将完全按计划运行。
由于 CPU 负载、其他任务等导致的延迟是可以预料的。
https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
The timer initialization steps
定时器初始化步骤
The task's timer nesting level is used both for nested calls to setTimeout(), and for the repeating timers created by setInterval(). (Or, indeed, for any combination of the two.) In other words, it represents nested invocations of this algorithm, not of a particular method.
任务的计时器嵌套级别既用于对 setTimeout() 的嵌套调用,也用于由 setInterval() 创建的重复计时器。 (或者,实际上,对于两者的任意组合。)换句话说,它表示此算法的嵌套调用,而不是特定方法的嵌套调用。
- Let thisArg be global if that is a WorkerGlobalScope object; otherwise let thisArg be the WindowProxy that corresponds to global.
- If previousId was given, let id be previousId; otherwise, let id be an implementation-defined integer that is greater than zero and does not already exist in global's map of active timers.
- If the surrounding agent's event loop's currently running task is a task that was created by this algorithm, then let nesting level be the task's timer nesting level. Otherwise, let nesting level be zero.
- If timeout is less than 0, then set timeout to 0.
- If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4.
- Let realm be global's relevant realm.
- Let initiating script be the active script.
- Assert: initiating script is not null, since this algorithm is always called from some script.
- Let task be a task that runs the following substeps:
- Increment nesting level by one.
- Set task's timer nesting level to nesting level.
- Let completionStep be an algorithm step which queues a global task on the timer task source given global to run task.
- Run steps after a timeout given global, "setTimeout/setInterval", timeout, completionStep, and id.
- Return id.
https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
使用 requestAnimationFrame
获取更可靠的
定时器`时间
// 性能优化
// 16ms ❓60Hz/s 刷新率, js 动画高性能更新策略
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
requestIdleCallback
Deferral of timeouts during pageload
延迟页面加载期间的超时
// 性能优化
https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#deferral_of_timeouts_during_pageload
https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
js 获取时间戳差值的小技巧
let startTime = new Date();
let endTime = new Date();
console.log(endTime - startTime);
// 7513
startTime;
// Thu Mar 02 2023 14:27:16 GMT+0800 (China Standard Time)
startTime - 0;
// 1677738436958
endTime;
// Thu Mar 02 2023 14:27:24 GMT+0800 (China Standard Time)
endTime - 0;
// 1677738444471
endTime - startTime;
// 7513
(🐞 反爬虫测试!打击盗版⚠️)如果你看到这个信息, 说明这是一篇剽窃的文章,请访问 https://www.cnblogs.com/xgqfrms/ 查看原创文章!
refs
https://www.cnblogs.com/xgqfrms/p/17170304.html#5154379
https://github.com/xgqfrms/learning/issues/144
©xgqfrms 2012-2021
www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!
原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/17171808.html
未经授权禁止转载,违者必究!