Различия "setInterval" между Chrome и другими браузерами. Почему?

КОД 1

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

Я не совсем понимаю результат.

КОД 2

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 и других браузерах отличается.

Я считаю результат в других браузерах правильным, поскольку он совпадает с моим здравым смыслом.

Добро пожаловать в Stack Overflow! Обращаясь за помощью, пожалуйста, найдите время, чтобы отформатировать код и сделать отступ в последовательном, удобочитаемом виде. (Это хорошая идея, когда нет тоже просит о помощи.) Я сделал это для вас в этом случае.

T.J. Crowder 01.05.2018 13:34

Вы предполагаете, что нулевой интервал сделает его синхронным? Если да, то это не так.

charlietfl 01.05.2018 13:36

По поводу "Код 2": «Результат этого кода, когда он запускается в браузерах Chrome и других браузерах, отличается». Что вы видите в каждом случае? Как вы думаете, какая версия здравого смысла?

T.J. Crowder 01.05.2018 13:38

Я бы предположил, что все другие библиотеки используют какой-то рекурсивный setTimeout для эмуляции setInterval, в то время как Chrome использует что-то еще (если это поведение действительно может быть воспроизведено)

Jonas Wilms 01.05.2018 13:39
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
4
215
1

Ответы 1

Да, 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

Cicely.Xin 01.05.2018 14:00

Связанный: Что они все делают предполагаемый: html.spec.whatwg.org/#timer-initialisation-steps

T.J. Crowder 01.05.2018 16:08

@ T.J.Crowder Существует открытый вопрос, который может привести к изменению спецификаций. У меня еще не было времени просмотреть все комментарии, так что я не уверен, в каком направлении будет развиваться текущая дискуссия. Но я обнаружил, что вместо задерживать мне следовало использовать дрейф, завтра буду редактировать с этими примечаниями.

Kaiido 01.05.2018 16:39

Другие вопросы по теме