Bagaimana saya bisa membuat setInterval juga berfungsi ketika sebuah tab tidak aktif di Chrome?

190

Saya memiliki setIntervalmenjalankan sepotong kode 30 kali per detik. Ini berfungsi dengan baik, namun ketika saya memilih tab lain (sehingga tab dengan kode saya menjadi tidak aktif), setIntervalini diatur ke keadaan siaga karena beberapa alasan.

Saya membuat test case yang disederhanakan ini ( http://jsfiddle.net/7f6DX/3/ ):

var $div = $('div');
var a = 0;

setInterval(function() {
    a++;
    $div.css("left", a)
}, 1000 / 30);

Jika Anda menjalankan kode ini dan kemudian beralih ke tab lain, tunggu beberapa detik dan kembali, animasi berlanjut pada titik ketika Anda beralih ke tab lain. Jadi animasi tidak berjalan 30 kali per detik jika tab tidak aktif. Ini dapat dikonfirmasi dengan menghitung berapa kali setIntervalfungsi dipanggil setiap detik - ini tidak akan menjadi 30 tetapi hanya 1 atau 2 jika tab tidak aktif.

Saya kira ini dilakukan oleh desain untuk meningkatkan kinerja, tetapi apakah ada cara untuk menonaktifkan perilaku ini? Ini sebenarnya merupakan kerugian dalam skenario saya.

pimvdb
sumber
3
Mungkin tidak, kecuali jika Anda meretasnya bersama dengan Dateobjek untuk benar-benar melihat waktu telah berlalu.
alex
Bisakah Anda menjelaskan lebih lanjut tentang skenario Anda? Mungkin itu tidak merugikan.
James
2
Maksud Anda perubahan kode ini , dan apa yang Anda tanyakan juga dibahas di sini - Oliver Mattos memposting beberapa pekerjaan, mungkin juga berlaku untuk Anda?
Shadow Wizard adalah Telinga Untuk Anda
6
Itu hampir selalu terbaik untuk animasi kunci pada jumlah waktu nyata berlalu sejak awal animasi, seperti yang dibaca dari Date. Sehingga ketika interval tidak menyala dengan cepat (untuk alasan ini atau lainnya) animasi menjadi semakin menyebalkan, tidak lebih lambat.
bobince
1
Judul pertanyaan membawa saya ke sini, tetapi kasus penggunaan saya sedikit berbeda - saya memerlukan token otentikasi yang diperbarui terlepas dari tidak aktifnya tab. Jika itu relevan untuk Anda, periksa jawaban saya di sini
Erez Cohen

Jawaban:

153

Pada sebagian besar browser, tab yang tidak aktif memiliki eksekusi prioritas rendah dan ini dapat memengaruhi pengatur waktu JavaScript.

Jika nilai transisi Anda dihitung menggunakan waktu nyata yang berlalu di antara bingkai alih-alih memperbaiki kenaikan pada setiap interval, Anda tidak hanya menyelesaikan masalah ini tetapi juga dapat mencapai animasi yang lebih baik dengan menggunakan requestAnimationFrame karena dapat mencapai 60fps jika prosesor tidak mencapai sangat sibuk.

Berikut ini adalah contoh JavaScript vanilla dari transisi properti animasi menggunakan 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>


Untuk Tugas Latar Belakang (terkait non-UI)

@UpTheCreek komentar:

Baik untuk masalah presentasi, tetapi masih ada beberapa hal yang perlu Anda jalankan.

Jika Anda memiliki tugas latar belakang yang perlu dieksekusi secara tepat pada interval yang diberikan, Anda dapat menggunakan Pekerja Web HTML5 . Lihatlah jawaban Möhre di bawah ini untuk lebih jelasnya ...

CSS vs JS "animasi"

Masalah ini dan banyak lainnya dapat dihindari dengan menggunakan transisi / animasi CSS alih-alih animasi berbasis JavaScript yang menambahkan overhead yang cukup besar. Saya akan merekomendasikan plugin jQuery ini yang memungkinkan Anda mengambil manfaat dari transisi CSS seperti animate()metode.

kbtzr
sumber
1
Saya mencoba ini di FireFox 5 dan 'setInterva'l masih berjalan bahkan ketika tab tidak fokus. Kode saya di 'setInterval' menggeser slidshow menggunakan 'animate ()'. Sepertinya animasi ini dibuat antri oleh FireFox. Ketika saya kembali pada tab, FireFox muncul antrian segera satu per satu menghasilkan tayangan slide yang bergerak cepat sampai antrian kosong.
nthpixel
@nthpixel akan menambahkan .stop (sesuai jawaban www) membantu dalam situasi tat (karena setiap kali itu akan menghapus animasi sebelumnya)
@ gordatron - .stop tidak membantu situasi saya. Perilaku yang sama. Dan saya menggunakan FireFox 6.0.2 sekarang. Saya bertanya-tanya apakah itu cara jQuery mengimplementasikan .animate.
nthpixel
@ cvsguimaraes - ya bagus. Apakah Anda tahu sesuatu dalam spesifikasi yang menjamin pekerja web akan berjalan bahkan saat berada di tab latar belakang? Saya agak khawatir bahwa vendor peramban akan secara sewenang-wenang memutuskan untuk menjepit ini di masa mendatang juga ...
UpTheCreek
Saya akan mengubah baris kode terakhir-terakhir untuk dibaca before = now;untuk mendapatkan waktu yang telah berlalu sejak awal, bukan akhir dari panggilan sebelumnya. Ini biasanya yang Anda inginkan saat menjiwai sesuatu. Seperti sekarang, jika eksekusi fungsi interval membutuhkan waktu 15 ms, elapsedTimeakan memberikan 35 ms (bukan 50 ms yang merupakan interval) lain kali dipanggil (saat tab aktif).
Jonas Berlin
71

Saya mengalami masalah yang sama dengan audio fading dan pemutar HTML5. Itu terhenti ketika tab menjadi tidak aktif. Jadi saya menemukan WebWorker diizinkan menggunakan interval / batas waktu tanpa batasan. Saya menggunakannya untuk mengirim "kutu" ke javascript utama.

Kode 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 Utama:

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;
    }
});
Möhre
sumber
6
Rapi! Sekarang mari kita berharap mereka tidak akan "memperbaiki" ini.
pimvdb
2
Bagus! Pendekatan ini harus digunakan ketika Anda benar-benar membutuhkan timer untuk bekerja tetapi tidak hanya untuk memperbaiki beberapa masalah animasi!
Konstantin Smolyanin
1
Saya membuat metronom yang hebat dengan ini. Menarik bahwa semua mesin drum html5 paling populer tidak menggunakan metode ini dan sebagai gantinya menggunakan setTimeout biasa yang lebih rendah. skyl.github.io/grimoire/fretboard-prototype.html - chrome atau safari memainkan yang terbaik, saat ini.
Skylar Saveland
Mereka akan "memperbaikinya" jika pengembang mulai menyalahgunakannya. Dengan pengecualian yang jarang terjadi, aplikasi Anda tidak begitu penting sehingga harus menghabiskan sumber daya di latar belakang untuk memberikan sedikit peningkatan dalam pengalaman pengguna.
Gunchars
41

Ada solusi untuk menggunakan Pekerja Web (seperti yang disebutkan sebelumnya), karena mereka berjalan dalam proses terpisah dan tidak melambat

Saya telah menulis skrip kecil yang dapat digunakan tanpa perubahan pada kode Anda - ini hanya mengabaikan fungsi setTimeout, clearTimeout, setInterval, clearInterval.

Cukup sertakan sebelum semua kode Anda.

info lebih lanjut di sini

Ruslan Tushov
sumber
1
Saya telah mengujinya pada proyek termasuk perpustakaan yang menggunakan timer (jadi saya tidak bisa mengimplementasikan pekerja sendiri). Itu bekerja seperti pesona. Terima kasih untuk perpustakaan ini
ArnaudR
@ ruslan-tushov baru saja mencobanya di chrome 60 tetapi tampaknya tidak berpengaruh sama sekali ... Saya memanggil beberapa fungsi dari beberapa setTimeout untuk menambah / menghapus elemen ke DOM yang dianimasikan oleh animasi css, dan saya melihat mereka dibekukan dengan beberapa penundaan setelah beralih tab (seperti biasa tanpa plugin)
neoDev
@neoDev Apakah Anda memuat HackTimer.js (atau HackTimer.min.js) sebelum JavaScript lainnya?
Davide Patti
@neoDev kedengarannya sangat aneh, karena saya baru-baru ini mencoba dan berfungsi
Davide Patti
@ DurdenP ya saya mencoba juga untuk meletakkannya sebelum JavaScript lainnya. Mungkin karena saya menggunakan animasi css? Saya ingat juga saya mencoba webworkers tetapi tidak berhasil
neoDev
13

Lakukan saja ini:

var $div = $('div');
var a = 0;

setInterval(function() {
    a++;
    $div.stop(true,true).css("left", a);
}, 1000 / 30);

Tab browser yang tidak aktif menyangga sebagian setIntervalatau setTimeoutfungsinya.

stop(true,true) akan menghentikan semua acara yang disangga dan menjalankan hanya animasi terakhir dengan segera.

The window.setTimeout()Metode sekarang klem untuk mengirim tidak lebih dari satu batas waktu per detik di tab yang tidak aktif. Selain itu, sekarang klem timeout bersarang ke nilai terkecil yang diizinkan oleh spesifikasi HTML5: 4 ms (bukan 10 ms yang digunakan untuk clamp).

www
sumber
1
Ini bagus untuk diketahui - misalnya untuk pembaruan ajax di mana data terbaru selalu benar - tetapi untuk pertanyaan yang diposting bukankah ini berarti bahwa animasi secara signifikan diperlambat / dijeda karena "a" tidak akan bertambah dalam jumlah yang tepat berapa kali ?
5

Saya pikir pemahaman terbaik tentang masalah ini adalah dalam contoh ini: http://jsfiddle.net/TAHDb/

Saya melakukan hal sederhana di sini:

Memiliki interval 1 detik dan setiap kali menyembunyikan rentang pertama dan memindahkannya ke yang terakhir, dan menunjukkan rentang ke-2.

Jika Anda tetap pada halaman itu berfungsi seperti yang seharusnya. Tetapi jika Anda menyembunyikan tab selama beberapa detik, ketika Anda kembali Anda akan melihat hal yang aneh.

Ini seperti semua acara yang tidak ucur selama waktu Anda tidak aktif sekarang akan terjadi semua dalam 1 waktu. jadi selama beberapa detik Anda akan mendapatkan seperti acara X. mereka begitu cepat sehingga memungkinkan untuk melihat semua 6 bentang sekaligus.

Jadi jahitan chrome hanya menunda acara, jadi ketika Anda kembali semua acara akan terjadi tetapi sekaligus ...

Sebuah aplikasi praktis adalah aplikasi ini untuk tampilan slide sederhana. Bayangkan angkanya menjadi Gambar, dan jika pengguna tetap dengan tab disembunyikan ketika kembali, ia akan melihat semua gambar mengambang, Benar-benar memuakkan.

Untuk memperbaikinya gunakan stop (true, true) seperti yang dikatakan pimvdb. Ini akan menghapus antrian acara.

1st4ck
sumber
4
Sebenarnya ini karena requestAnimationFramejQuery yang digunakan. Karena beberapa keanehan yang dimilikinya (seperti ini), mereka menghapusnya. Di 1,7 Anda tidak melihat perilaku ini. jsfiddle.net/TAHDb/1 . Karena intervalnya sekali per detik ini sebenarnya tidak mempengaruhi masalah yang saya posting, karena maksimum untuk tab tidak aktif juga sekali per detik - sehingga tidak ada bedanya.
pimvdb
4

Bagi saya itu tidak penting untuk memutar audio di latar belakang seperti untuk orang lain di sini, masalah saya adalah bahwa saya memiliki beberapa animasi dan mereka bertindak seperti orang gila ketika Anda berada di tab lain dan kembali kepada mereka. Solusi saya adalah meletakkan animasi ini di dalam jika itu mencegah tab tidak aktif :

if (!document.hidden){ //your animation code here }

berkat itu animasi saya hanya berjalan jika tab aktif. Saya harap ini akan membantu seseorang dengan kasus saya.

Tomaaszq
sumber
1
Saya menggunakannya seperti ini setInterval(() => !document.hidden && myfunc(), 1000)dan berfungsi dengan baik
Didi Bear
4

Keduanya setIntervaldan requestAnimationFrametidak berfungsi saat tab tidak aktif atau berfungsi tetapi tidak pada periode yang tepat. Solusinya adalah menggunakan sumber lain untuk peristiwa waktu. Misalnya soket web atau pekerja web adalah dua sumber acara yang berfungsi dengan baik saat tab tidak aktif. Jadi tidak perlu memindahkan semua kode Anda ke pekerja web, cukup gunakan pekerja sebagai sumber peristiwa waktu:

// worker.js
setInterval(function() {
    postMessage('');
}, 1000 / 50);

.

var worker = new Worker('worker.js');
var t1 = 0;
worker.onmessage = function() {
    var t2 = new Date().getTime();
    console.log('fps =', 1000 / (t2 - t1) | 0);
    t1 = t2;
}

Link jsfiddle dari sampel ini.

iman
sumber
"gunakan saja pekerja sebagai sumber acara waktu" Terima kasih atas idenya
Promod Sampath Elvitigala
1

Sangat dipengaruhi oleh perpustakaan Ruslan Tushov, saya telah membuat perpustakaan kecil saya sendiri . Cukup tambahkan skrip di dalam <head>dan itu akan menambal setIntervaldan setTimeoutdengan yang digunakan WebWorker.

Mariy
sumber
baru saja mencobanya di chrome 60 tetapi tampaknya tidak berpengaruh sama sekali ... Saya memanggil beberapa fungsi dari beberapa setTimeout untuk menambah / menghapus elemen ke DOM yang dianimasikan oleh animasi css, dan saya melihat mereka dibekukan dengan beberapa penundaan setelah beralih tab (seperti biasa tanpa plugin)
neoDev
Saya menggunakannya dengan Chrome 60. Bisakah Anda membuat demo dan mempostingnya di masalah github?
Mariy
1

Memutar file audio memastikan kinerja Javascript latar belakang penuh untuk saat ini

Bagi saya, itu adalah solusi paling sederhana dan paling tidak mengganggu - selain memainkan suara yang samar / hampir kosong, tidak ada efek samping potensial lainnya

Anda dapat menemukan detailnya di sini: https://stackoverflow.com/a/51191818/914546

(Dari jawaban lain, saya melihat bahwa beberapa orang menggunakan properti berbeda dari tag Audio, saya bertanya-tanya apakah mungkin menggunakan tag Audio untuk kinerja penuh, tanpa benar-benar memainkan sesuatu)

Kaan Soral
sumber
0

catatan: solusi ini tidak cocok jika interval Anda suka bekerja di latar belakang, misalnya, memutar audio atau ... tetapi jika Anda bingung misalnya tentang animasi Anda tidak berfungsi dengan benar ketika kembali ke halaman Anda (tab) ini adalah solusi yang bagus.

Ada banyak cara untuk mencapai tujuan ini, mungkin "WebWorkers" adalah yang paling standar tetapi tentu saja, itu bukan yang termudah dan praktis, terutama Jika Anda tidak punya cukup waktu, maka Anda dapat mencoba cara ini:

► KONSEP BASIS:

1- membuat nama untuk interval Anda (atau animasi) dan mengatur interval Anda (animasi), sehingga akan berjalan ketika pengguna pertama kali membuka halaman Anda: var interval_id = setInterval(your_func, 3000);

2- oleh $(window).focus(function() {});dan $(window).blur(function() {});Anda dapat clearInterval(interval_id)setiap browser (tab) dinonaktifkan dan Jalankan kembali interval Anda (animasi) setiap browser (tab) akan aktif kembali denganinterval_id = setInterval();

►CODE KODE:

var interval_id = setInterval(your_func, 3000);

$(window).focus(function() {
    interval_id = setInterval(your_func, 3000);
});
$(window).blur(function() {
    clearInterval(interval_id);
    interval_id = 0;
});
ashkan nasirzadeh
sumber
0

Ini pertanyaan yang cukup lama tapi saya mengalami masalah yang sama.
Jika Anda menjalankan web di chrome, Anda dapat membaca posting ini Background Tabs di Chrome 57 .

Pada dasarnya timer interval bisa berjalan jika belum habis dari anggaran timer.
Konsumsi anggaran didasarkan pada penggunaan waktu CPU dari tugas di dalam timer.
Berdasarkan skenario saya, saya menggambar video ke kanvas dan transportasi ke WebRTC.
Koneksi video webrtc akan terus memperbarui bahkan tab tidak aktif.

Namun Anda harus menggunakan setIntervalbukan requestAnimationFrame.
tidak direkomendasikan untuk rendering UI.

Akan lebih baik untuk mendengarkan visibilityChangeacara dan mengubah membuat mekanisme.

Selain itu, Anda dapat mencoba @ kaan-soral dan itu harus berfungsi berdasarkan dokumentasi.

鄭元傑
sumber
-1

Inilah solusi kasar saya

(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;
})();
Tengiz
sumber
The postMessageHandlermemanggil acara sendiri itu adalah penanganan, sehingga memiliki loop tak terbatas yang tidak menghalangi UI. Dan sementara itu berulang tanpa henti itu memeriksa dengan setiap penanganan acara jika ada fungsi timeout atau interval untuk dijalankan.
Diciptakan pada
-1

Saya dapat memanggil fungsi panggilan balik saya minimal 250ms menggunakan tag audio dan menangani acara pembaruan waktu. Disebut 3-4 kali dalam satu detik. Lebih baik dari setTimeout lagging kedua

pengguna7356139
sumber