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
*/
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
}
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
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
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