console.info('start');
const interval = setInterval(() => {
console.info('setInterval');
}, 0);
setTimeout(() => {
console.info('setTimeout 1');
Promise.resolve()
.then(() => {
console.info('promise 3');
})
.then(() => {
console.info('promise 4');
})
.then(() => {
setTimeout(() => {
console.info('setTimeout 2');
Promise.resolve()
.then(() => {
console.info('promise 5');
})
.then(() => {
console.info('promise 6');
})
.then(() => {
clearInterval(interval);
});
}, 0);
});
}, 0);
Promise.resolve()
.then(() => {
console.info('promise 1');
})
.then(() => {
console.info('promise 2');
});
Результат этого кода в браузерах Windows Edge / Mac Safari / Опера следующий:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6
Но при запуске в Chrome на Windows или Mac результат имеет два случая.
Иногда результат такой же, как указано выше.
Иногда результат выводит два 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.info((new Date()).getTime());
while(k < 10000){
for(var j = 2; j < k; j++){
if (k%j == 0){
break;
}
if (j == k-1){
console.info(k)
}
}
k++;
}
console.info((new Date()).getTime());
}
var t = setInterval(expendTime,15,3);
setTimeout(function() {
clearInterval(t);
},30);
.as-console-wrapper {
max-height: 100% !important;
}
Результат этого кода при запуске в Chrome и других браузерах отличается.
Я считаю результат в других браузерах правильным, поскольку он совпадает с моим здравым смыслом.
Вы предполагаете, что нулевой интервал сделает его синхронным? Если да, то это не так.
По поводу "Код 2": «Результат этого кода, когда он запускается в браузерах Chrome и других браузерах, отличается». Что вы видите в каждом случае? Как вы думаете, какая версия здравого смысла?
Я бы предположил, что все другие библиотеки используют какой-то рекурсивный setTimeout
для эмуляции setInterval
, в то время как Chrome использует что-то еще (если это поведение действительно может быть воспроизведено)
Да, chrome * реализация setInterval
самокорректируется, поэтому его вызовы срабатывают с точными интервалами, устраняя любой дрейф.
* and maybe Edge's one ?
Вот пример кода, который реализует такой корректирующий дрейф метод 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>
Обратите внимание, что текущие спецификации читается как реализация Firefox и как то, что вы поняли, то есть рекурсивный setTimeout, без учета дрейфа.
Действительно, шаги инициализации таймера требует, чтобы, когда флаг повторить установлен в значение true, те же аргументы должны быть переданы следующему методу шаги инициализации таймера.
Но открытый вопрос обсуждает именно эту проблему, а мощь приводит к пересмотру спецификаций, так что UA рекомендуется применять такую коррекцию дрейфа, когда это возможно.
Я думаю, что этот ответ мне подходит, пожалуйста, не удаляйте. Я это читаю. сейчас @ dippas
Связанный: Что они все делают предполагаемый: html.spec.whatwg.org/#timer-initialisation-steps
@ T.J.Crowder Существует открытый вопрос, который может привести к изменению спецификаций. У меня еще не было времени просмотреть все комментарии, так что я не уверен, в каком направлении будет развиваться текущая дискуссия. Но я обнаружил, что вместо задерживать мне следовало использовать дрейф, завтра буду редактировать с этими примечаниями.
Добро пожаловать в Stack Overflow! Обращаясь за помощью, пожалуйста, найдите время, чтобы отформатировать код и сделать отступ в последовательном, удобочитаемом виде. (Это хорошая идея, когда нет тоже просит о помощи.) Я сделал это для вас в этом случае.