看了很多技术文章,大多都信誓旦旦地说setTimeout(f, 0)语句中的f方法会在4ms之后执行,以至于小组很多成员都深信不疑,我决定亲手来验证一下4ms的真伪,代码如下:
// 定义单次调用函数
function testTimeout() {
// 启动任务计时器
console.time("Timeout")
// 启动浏览器定时任务
setTimeout(function() {
// 统计任务消耗的时间
console.timeEnd("Timeout")
}, 0)
}
// 调用函数
testTimeout();
为了消除任务队列的干扰,我在代码里消除了其他所有的JS代码,仅仅保留了此段代码,然而运行结果依旧让我大吃一惊,消耗的时间竟然高达87ms,有时甚至达到了153ms。
满脑子都是问号,时间都去哪儿了?时间都去哪儿了?时间都去哪儿了?
继续改进自己的测试代码,添加多次定时任务,看看它们之间的间隔是否依旧这么惊人,如下:
// 添加循环时间任务
// count : Number 循环次数
function testTimeouts(count) {
// 开始计时
console.time("Timeouts");
// 派发单次任务
function testTimeout() {
if(count > -1) {
// 派发循环任务
setTimeout(testTimeout, 0)
count --;
} else {
// 结束计时
console.timeEnd("Timeouts")
}
}
testTimeout();
}
// 执行10计时计算
testTimeouts(10);
执行结果依旧让我难以理解,只有100ms,执行十次setTimeout任务竟然又只有100ms,怎么看与前面单次的测算结果也不存在乘数关系,这是怎么了?
继续执行20次、30次、40次的定时任务,返回的结果分别是171、215、269,好像依旧没有什么明确的等差关系,模模糊糊觉得可能有50ms的时差,50ms除以10,难道这就是真相?
再次改进测试代码,在页面添加一个id为test按钮,然后为此按钮注册事件,代码如下:
document.getElementById("test").addEventListener("click", function() {
// 执行单次定时任务
testTimeout();
// 执行多次定时任务
//testTimeouts(10);
});
这次的测试结果更让我吃惊,单次定时任务仅仅只需要1ms左右的时间,10次、20次、30次、40次的执行时间分别为32ms、72ms、113ms与154ms,难道这次循环定时就是4ms的铁证?
总结
1. setTimeout与4ms真的没什么关系,仅仅在最后循环定时任务中表现出了很大约的约等于关系;
2. setTimeout时间参数的准确性存疑,并不会严格按照约定的时间执行;
3. setTimeout的定时任务机制容易受到任务队列的影响,可参见下面的补充测试;
消失的时间去了哪里?如何保证时间任务的准确性?多个时间任务并发时,浏览器又有多少个时间任务队列?请看后续文章setTimeout初探(二):延迟与跳帧
补充测试
function testTimeout(label) {
console.time(label)
setTimeout(function() {
console.timeEnd(label)
}, 0)
}
// S1需要的时间为97ms
testTimeout("S1");
window.onload = function() {
// S2需要的时间仅为64ms
testTimeout("S2");
}