Как я могу заставить setInterval также работать, когда вкладка неактивна в Chrome?
у меня есть setInterval запуск фрагмента кода 30 раз в секунду. Это отлично работает, однако, когда я выбираю другую вкладку (так что вкладка с моим кодом становится неактивным),setInterval по какой-то причине устанавливается в состояние ожидания.
Я сделал этот упрощенный тестовый случай (http://jsfiddle.net/7f6DX/3/):
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.css("left", a)
}, 1000 / 30);
если вы запустите этот код, а затем переключитесь на другую вкладку, подождите несколько секунд и вернитесь назад, анимация продолжится в тот момент, когда вы переключился на другую вкладку. Таким образом, анимация не выполняется 30 раз в секунду, если вкладка неактивна. Это можно подтвердить, подсчитав количество раз setInterval функция вызывается каждую секунду - это будет не 30, а только 1 или 2, если вкладка неактивна.
Я думаю, что это делается по дизайну, чтобы улучшить производительность, но есть ли способ отключить это поведение? Это на самом деле недостаток в моем случае.
9 ответов:
в большинстве браузеров неактивные вкладки имеют низкий приоритет выполнения, и это может повлиять на таймеры в JavaScript.
если значения вашего перехода были рассчитаны с использованием Реальное время, прошедшее между кадрами вместо фиксированных приращений на каждом интервале, вы не только обойти эту проблему, но и может достичь удушающей анимации с помощью requestAnimationFrame как он может получить до 60 кадров в секунду, если процессор не очень занят.
здесь ванильный JavaScript пример анимированного перехода свойств с помощью
requestAnimationFrame:var target = document.querySelector('div#target') var startedAt, duration = 3000 var domain = [-100, window.innerWidth] var range = domain[1] - domain[0] function start() { startedAt = Date.now() updateTarget(0) requestAnimationFrame(update) } function update() { let elapsedTime = Date.now() - startedAt // playback is a value between 0 and 1 // being 0 the start of the animation and 1 its end let playback = elapsedTime / duration updateTarget(playback) if (playback > 0 && playback < 1) { // Queue the next frame requestAnimationFrame(update) } else { // Wait for a while and restart the animation setTimeout(start, duration/10) } } function updateTarget(playback) { // Uncomment the line below to reverse the animation // playback = 1 - playback // Update the target properties based on the playback position let position = domain[0] + (playback * range) target.style.left = position + 'px' target.style.top = position + 'px' target.style.transform = 'scale(' + playback * 3 + ')' } start()body { overflow: hidden; } div { position: absolute; white-space: nowrap; }<div id="target">...HERE WE GO</div>
для фоновых задач (не связанных с UI)
@UpTheCreek комментарий:
отлично подходит для презентации вопросов, но все же есть некоторые вещи, которые вам нужно продолжать работать.
если у вас есть фоновые задачи, что должен для точного выполнения с заданными интервалами, вы можно использовать HTML5 Web Workers. Взгляните на ответ мере ниже для более подробной информации...
CSS vs JS "анимация"
эта проблема и и многие другие можно было бы избежать, используя CSS-переходы / анимации вместо анимации на основе JavaScript, что добавляет значительные накладные расходы. Я бы рекомендовал это плагин jQuery что давайте вы извлекаете выгоду из CSS переходов так же, как
animate()методы.
я столкнулся с той же проблемой с замиранием звука и HTML5-плеером. Он застрял, когда вкладка стала неактивной. Поэтому я узнал, что веб-работнику разрешено использовать интервалы/таймауты без ограничений. Я использую его для публикации "галочек" в основном javascript.
Код WebWorkers:
var fading = false; var interval; self.addEventListener('message', function(e){ switch (e.data) { case 'start': if (!fading){ fading = true; interval = setInterval(function(){ self.postMessage('tick'); }, 50); } break; case 'stop': clearInterval(interval); fading = false; break; }; }, false);Основной Javascript:
var player = new Audio(); player.fader = new Worker('js/fader.js'); player.faderPosition = 0.0; player.faderTargetVolume = 1.0; player.faderCallback = function(){}; player.fadeTo = function(volume, func){ console.log('fadeTo called'); if (func) this.faderCallback = func; this.faderTargetVolume = volume; this.fader.postMessage('start'); } player.fader.addEventListener('message', function(e){ console.log('fader tick'); if (player.faderTargetVolume > player.volume){ player.faderPosition -= 0.02; } else { player.faderPosition += 0.02; } var newVolume = Math.pow(player.faderPosition - 1, 2); if (newVolume > 0.999){ player.volume = newVolume = 1.0; player.fader.postMessage('stop'); player.faderCallback(); } else if (newVolume < 0.001) { player.volume = newVolume = 0.0; player.fader.postMessage('stop'); player.faderCallback(); } else { player.volume = newVolume; } });
существует решение для использования веб-работников (как упоминалось ранее), потому что они работают в отдельном процессе и не замедляются
Я написал крошечный скрипт, который можно использовать без изменений в коде - он просто переопределяет функции setTimeout, clearTimeout, setInterval, clearInterval.
просто включите его перед всем вашим кодом.
просто сделать это:
var $div = $('div'); var a = 0; setInterval(function() { a++; $div.stop(true,true).css("left", a); }, 1000 / 30);неактивные вкладки браузера, буфера некоторых
setIntervalилиsetTimeoutфункции.
stop(true,true)остановит все буферизованные события и немедленно выполнит только последнюю анимацию.The
window.setTimeout()метод теперь зажимает для отправки не более одного таймаута в секунду в неактивных вкладках. Кроме того, теперь он зажимает вложенные таймауты до наименьшего значения, разрешенного спецификацией HTML5: 4 мс (вместо 10 мс, которые он использовал для зажима).
Я думаю, что лучшее понимание этой проблемы находится в этом примере:http://jsfiddle.net/TAHDb/
Я делаю здесь простую вещь:
имеют интервал 1 сек и каждый раз скрывают первый пролет и перемещают его на последний, а также показывают 2-й пролет.
Если вы остаетесь на странице, он работает как предполагается. Но если вы спрячете вкладку на несколько секунд, когда вы вернетесь, вы увидите проветриваемую вещь.
его как и все события, которые не ucur в то время, когда вы были неактивны теперь будет ocur все в 1 раз. так что в течение нескольких секунд вы получите как X событий. они так быстро, что его можно увидеть все 6 пролетов сразу.
Так что это швы chrome только задерживает события, так что когда вы вернетесь все события будут происходить, но все сразу...
было практическое применение этих неблагоприятных событий МКС для простого слайдшоу. Представьте себе, что числа являются изображениями, и если пользователь останется со скрытой вкладкой, когда он вернется, он увидит все imgs плавает, полностью заворожен.
чтобы исправить это,используйте стоп(true, true), как сказал pimvdb. Это очистит очередь событий.
под сильным влиянием библиотеки Руслана Тушова, я создал свой собственный маленький библиотека. Просто добавьте скрипт в
<head>и это будет патчsetIntervalиsetTimeoutС теми, которые используютWebWorker.
вот мое грубое решение
(function(){ var index = 1; var intervals = {}, timeouts = {}; function postMessageHandler(e) { window.postMessage('', "*"); var now = new Date().getTime(); sysFunc._each.call(timeouts, function(ind, obj) { var targetTime = obj[1]; if (now >= targetTime) { obj[0](); delete timeouts[ind]; } }); sysFunc._each.call(intervals, function(ind, obj) { var startTime = obj[1]; var func = obj[0]; var ms = obj[2]; if (now >= startTime + ms) { func(); obj[1] = new Date().getTime(); } }); } window.addEventListener("message", postMessageHandler, true); window.postMessage('', "*"); function _setTimeout(func, ms) { timeouts[index] = [func, new Date().getTime() + ms]; return index++; } function _setInterval(func, ms) { intervals[index] = [func, new Date().getTime(), ms]; return index++; } function _clearInterval(ind) { if (intervals[ind]) { delete intervals[ind] } } function _clearTimeout(ind) { if (timeouts[ind]) { delete timeouts[ind] } } var intervalIndex = _setInterval(function() { console.log('every 100ms'); }, 100); _setTimeout(function() { console.log('run after 200ms'); }, 200); _setTimeout(function() { console.log('closing the one that\'s 100ms'); _clearInterval(intervalIndex) }, 2000); window._setTimeout = _setTimeout; window._setInterval = _setInterval; window._clearTimeout = _clearTimeout; window._clearInterval = _clearInterval; })();
воспроизведение аудиофайла обеспечивает полную фоновую производительность Javascript на данный момент
для меня это было самое простое и наименее навязчивое решение-помимо воспроизведения слабого / почти пустого звука, нет никаких других потенциальных побочных эффектов
вы можете найти подробности здесь: https://stackoverflow.com/a/51191818/914546
(из других ответов, я вижу, что некоторые люди используют разные свойства аудио тегов, а есть ли можно использовать звуковой тег для полной производительности, фактически не играя что-то)
Я смог вызвать свою функцию обратного вызова как минимум на 250 мс, используя звуковой тег и обрабатывая его событие ontimeupdate. Его называют 3-4 раза в секунду. Его лучше, чем одна секунда отставания setTimeout
Comments