console.log('start');
const interval = setInterval(() => {
console.log('setInterval');
}, 0);
setTimeout(() => {
console.log('setTimeout 1');
Promise.resolve()
.then(() => {
console.log('promise 3');
})
.then(() => {
console.log('promise 4');
})
.then(() => {
setTimeout(() => {
console.log('setTimeout 2');
Promise.resolve()
.then(() => {
console.log('promise 5');
})
.then(() => {
console.log('promise 6');
})
.then(() => {
clearInterval(interval);
});
}, 0);
});
}, 0);
Promise.resolve()
.then(() => {
console.log('promise 1');
})
.then(() => {
console.log('promise 2');
});
此代码在 Windows Edge / Mac Safari / Opera 浏览器上的结果如下:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6
但是当在Windows或Mac上运行chrome时,结果有两种情况。
有时结果与上述相同。
有时结果输出两个setInterval
如下:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setInterval
setTimeout 2
promise 5
promise 6
我对结果感到困惑。
function expendTime(k){
console.log((new Date()).getTime());
while(k < 10000){
for(var j = 2; j < k; j++){
if(k%j == 0){
break;
}
if(j == k-1){
console.log(k)
}
}
k++;
}
console.log((new Date()).getTime());
}
var t = setInterval(expendTime,15,3);
setTimeout(function() {
clearInterval(t);
},30);
.as-console-wrapper {
max-height: 100% !important;
}
在chrome和其他浏览器上运行时此代码的结果不同。
我认为其他浏览器的结果是正确的,因为它与我的常识一致。
答案 0 :(得分:1)
是chrome's* implementation of setInterval
会自我纠正,因此其呼叫会以确切的间隔触发,消除任何漂移
* 和Edge的一个?
下面是一个示例代码,它实现了这样一个漂移校正setInterval方法,在chrome中运行,你可以看到它具有与内置函数相同的效果,而其他实现和递归setTimeout将继续增加漂移。
// drift-correcting setInterval
// returns an object which "_id" property is the inner timeout id, so it can be canceled by clearInterval
function driftCorrectingInterval(cb, delay) {
var begin = performance.now(), // what time is it?
result = {
_id: setTimeout(inner, delay)
},
passed = true; // a flag to avoid try-catch the callback
return result;
function inner() {
if (!passed) return; // 'cb' thrown
passed = false; // set up the callback trap
var now = performance.now(),
drift = (now - begin) - delay;
begin += delay; // start a new interval
result._id = setTimeout(inner, delay - drift);
// call it at the end so we can cancel inside the callback
cb();
passed = true; // didn't throw we can continue
}
}
// snippet-only tests
function test(ms) {
function setTimeoutLoop(cb, ms) {
function loop() {
cb();
setTimeout(loop, ms);
}
setTimeout(loop, ms);
}
var now = performance.now(),
built_in_prev = now,
timeout_prev = now,
sCI_prev = now,
built_in_elem = document.querySelector('#test_' + ms + ' .delay.built_in'),
timeout_elem = document.querySelector('#test_' + ms + ' .delay.timeout'),
sCI_elem = document.querySelector('#test_' + ms + ' .delay.sCI');
setInterval(() => {
var now = performance.now(),
delay = (now - built_in_prev) - ms;
built_in_prev += ms;
built_in_elem.textContent = Math.round(delay);
}, ms);
setTimeoutLoop(() => {
var now = performance.now(),
delay = (now - timeout_prev) - ms;
timeout_prev += ms;
timeout_elem.textContent = Math.round(delay);
}, ms);
driftCorrectingInterval(() => {
var now = performance.now(),
delay = (now - sCI_prev) - ms;
sCI_prev += ms;
sCI_elem.textContent = Math.round(delay);
}, ms);
}
test(1000);
[id^='test'] {
border: 1px solid;
padding: 0 12px
}
<div id="test_1000">
<p>built in setInterval drift: <span class="delay built_in">0</span>ms</p>
<p>built in setTimeout loop drift: <span class="delay timeout">0</span>ms</p>
<p>driftCorrectingInterval drift: <span class="delay sCI">0</span>ms</p>
</div>
请注意current specs读作Firefox的实现,以及您理解的内容,即递归的setTimeout,而不考虑漂移。
确实timer initialization steps要求当 repeat 标志设置为true时,应将相同的参数传递给下一个计时器初始化步骤方法。
但open issue讨论了这个问题,可能导致规范的修订,以便鼓励UA尽可能应用这种漂移校正。