Làm cách nào tôi có thể làm cho setInterval cũng hoạt động khi một tab không hoạt động trong Chrome?


189

Tôi có một setIntervalđoạn mã chạy 30 lần một giây. Điều này hoạt động rất tốt, tuy nhiên khi tôi chọn một tab khác (để tab có mã của tôi không hoạt động), thì setIntervalnó được đặt ở trạng thái không hoạt động vì một số lý do.

Tôi đã thực hiện trường hợp thử nghiệm đơn giản hóa này ( http://jsfiddle.net/7f6DX/3/ ):

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

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

Nếu bạn chạy mã này và sau đó chuyển sang tab khác, đợi vài giây và quay lại, hoạt ảnh sẽ tiếp tục tại thời điểm đó là khi bạn chuyển sang tab khác. Vì vậy, hình ảnh động không chạy 30 lần một giây trong trường hợp tab không hoạt động. Điều này có thể được xác nhận bằng cách đếm số lần setIntervalhàm được gọi mỗi giây - đây sẽ không phải là 30 mà chỉ là 1 hoặc 2 nếu tab không hoạt động.

Tôi đoán rằng điều này được thực hiện bằng thiết kế để cải thiện hiệu suất, nhưng có cách nào để vô hiệu hóa hành vi này không? Nó thực sự là một bất lợi trong kịch bản của tôi.


3
Có lẽ là không, trừ khi bạn hack nó cùng với Dateđối tượng để thực sự thấy thời gian đã trôi qua.
alex

Bạn có thể giải thích thêm về kịch bản của bạn? Có lẽ nó không phải là một bất lợi như vậy.
James

2
Bạn có nghĩa là thay đổi mã này , và những gì bạn yêu cầu cũng được thảo luận ở đây - Oliver Mattos đã đăng một số công việc xung quanh, có lẽ nó cũng hợp lệ trong trường hợp của bạn?
Shadow Wizard là Ear For You

6
Hầu như luôn luôn tốt nhất cho các hình ảnh động chính về thời lượng thực đã trôi qua kể từ khi bắt đầu hoạt hình, như đã đọc từ Date. Vì vậy, khi các khoảng thời gian không kích hoạt nhanh (vì lý do này hoặc lý do khác), hoạt hình sẽ bị giật hơn, không chậm hơn.
bobince

1
Tiêu đề của câu hỏi đã dẫn tôi đến đây tuy nhiên trường hợp sử dụng của tôi hơi khác một chút - tôi cần một mã thông báo xác thực được làm mới bất kể tab không hoạt động. Nếu điều đó có liên quan đến bạn, hãy xem câu trả lời của tôi ở đây
Erez Cohen

Câu trả lời:


152

Trên hầu hết các trình duyệt, các tab không hoạt động có mức độ thực thi ưu tiên thấp và điều này có thể ảnh hưởng đến bộ định thời JavaScript.

Nếu các giá trị chuyển đổi của bạn được tính bằng thời gian thực trôi qua giữa các khung thay vì gia tăng cố định trên mỗi khoảng thời gian, bạn không chỉ khắc phục vấn đề này mà còn có thể đạt được hiệu ứng động mượt mà bằng cách sử dụng requestAnimationFrame vì nó có thể đạt tới 60fps nếu bộ xử lý không rất bận.

Đây là một ví dụ JavaScript vanilla về chuyển đổi thuộc tính hoạt hình bằng cách sử dụng 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>


Đối với Tác vụ nền (không liên quan đến giao diện người dùng)

@UpTheCalet bình luận:

Tốt cho các vấn đề trình bày, nhưng vẫn có một số điều mà bạn cần tiếp tục chạy.

Nếu bạn có các tác vụ nền cần được thực hiện chính xác theo các khoảng thời gian nhất định, bạn có thể sử dụng Công cụ web HTML5 . Hãy xem câu trả lời của Möhre dưới đây để biết thêm chi tiết ...

"Hoạt hình" CSS vs JS

Vấn đề này và nhiều vấn đề khác có thể tránh được bằng cách sử dụng chuyển tiếp / hoạt hình CSS thay vì hoạt ảnh dựa trên JavaScript có thêm chi phí đáng kể. Tôi muốn giới thiệu plugin jQuery này để cho phép bạn hưởng lợi từ các chuyển đổi CSS giống như các animate()phương thức.


1
Tôi đã thử điều này trên FireFox 5 và 'setInterva'l vẫn chạy ngay cả khi tab không được lấy nét. Mã của tôi trong 'setInterval' trượt một slide bằng cách sử dụng 'animate ()'. Có vẻ như hoạt hình được xếp hàng bởi FireFox. Khi tôi quay trở lại trên tab, FireFox bật hàng đợi ngay lập tức lần lượt dẫn đến trình chiếu di chuyển nhanh cho đến khi hàng đợi trống.
nthpixel

@nthpixel sẽ thêm .stop (theo câu trả lời của www) trong tình huống tat (vì mỗi lần nó sẽ xóa các hình động trước đó)

@gordatron - .stop không giúp gì được cho tình huống của tôi. Hành vi tương tự. Và tôi đang dùng FireFox 6.0.2. Tôi tự hỏi nếu đó là cách jQuery thực hiện .animate.
nthpixel

@cvsguimaraes - vâng điểm tốt. Bạn có biết bất cứ điều gì trong thông số kỹ thuật đảm bảo nhân viên web sẽ chạy ngay cả khi trong tab nền không? Tôi hơi lo lắng rằng các nhà cung cấp trình duyệt sẽ tự ý quyết định kẹp những thứ này trong tương lai ...
UpTheCalet

Tôi sẽ thay đổi dòng mã tiếp theo cuối cùng để đọc before = now;để có thời gian trôi qua kể từ khi bắt đầu thay vì kết thúc cuộc gọi trước đó. Đây thường là những gì bạn muốn khi hoạt hình mọi thứ. Như hiện tại, nếu việc thực thi hàm khoảng thời gian mất 15ms, elapsedTimesẽ cho 35ms (thay vì 50ms là khoảng thời gian) vào lần tiếp theo khi nó được gọi (khi tab đang hoạt động).
Jonas Berlin

71

Tôi gặp vấn đề tương tự với âm thanh mờ dần và trình phát HTML5. Nó bị kẹt khi tab không hoạt động. Vì vậy, tôi phát hiện ra một WebWorker được phép sử dụng khoảng thời gian / thời gian chờ mà không giới hạn. Tôi sử dụng nó để đăng "tick" lên javascript chính.

Mã 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 chính:

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;
    }
});

6
Khéo léo! Bây giờ hãy hy vọng họ sẽ không "sửa chữa" điều này.
pimvdb

2
Tuyệt quá! Cách tiếp cận này nên được sử dụng khi bạn chính xác cần bộ định thời hoạt động nhưng không chỉ để khắc phục một số vấn đề về hoạt hình!
Konstantin Smolyanin

1
Tôi đã thực hiện một metronome tuyệt vời với điều này. Điều thú vị là tất cả các máy trống html5 phổ biến nhất đều không sử dụng phương pháp này và thay vào đó sử dụng setTimeout thông thường kém hơn. skyl.github.io/grimoire/fretboard-prototype.html - chrome hoặc safari chơi nó tốt nhất, ngay bây giờ.
Skylar Saveland

Họ sẽ "sửa" nó nếu nhà phát triển bắt đầu lạm dụng nó. Với các trường hợp ngoại lệ hiếm hoi, ứng dụng của bạn không quan trọng đến mức phải tiêu thụ tài nguyên trong nền để cung cấp một số cải tiến tối thiểu trong trải nghiệm người dùng.
Gunchars

41

Có một giải pháp để sử dụng Web Worker (như đã đề cập trước đó), vì chúng chạy trong quy trình riêng biệt và không bị chậm lại

Tôi đã viết một tập lệnh nhỏ có thể được sử dụng mà không cần thay đổi mã của bạn - nó chỉ đơn giản ghi đè các hàm setTimeout, ClearTimeout, setInterval, clearInterval.

Chỉ cần bao gồm nó trước tất cả mã của bạn.

thêm thông tin ở đây


1
Tôi đã thử nghiệm nó trên một dự án bao gồm các thư viện đang sử dụng bộ tính giờ (vì vậy tôi không thể tự thực hiện công nhân). Nó làm việc như một say mê. Cảm ơn thư viện này
ArnaudR 7/10/2015

@ ruslan-tushov chỉ thử nó trên chrome 60 nhưng dường như không có tác dụng gì cả ... Tôi đang gọi một số chức năng từ một số setTimeout để thêm / xóa các thành phần vào DOM được hoạt hình bằng hoạt hình css và tôi thấy chúng bị đóng băng với một số độ trễ sau khi chuyển đổi tab (giống như bình thường không có plugin)
neoDev

@neoDev Bạn có tải HackTimer.js (hoặc HackTimer.min.js) trước bất kỳ JavaScript nào khác không?
Davide Patti

@neoDev Nghe có vẻ rất lạ, vì gần đây tôi đã thử và nó hoạt động
Davide Patti

@DurdenP có, tôi cũng đã cố gắng đặt nó trước bất kỳ JavaScript nào khác. Có lẽ bởi vì tôi đã sử dụng hình ảnh động css? Tôi cũng nhớ tôi đã thử webworkers nhưng không có may mắn
neoDev

13

Chỉ cần làm điều này:

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

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

Các tab trình duyệt không hoạt động đệm một số setIntervalhoặc các setTimeoutchức năng.

stop(true,true) sẽ dừng tất cả các sự kiện được đệm và thực hiện ngay chỉ hoạt hình cuối cùng.

Các window.setTimeout()phương pháp hiện nay kẹp để gửi không quá một thời gian chờ mỗi giây trong các tab không hoạt động. Ngoài ra, giờ đây nó kẹp thời gian chờ lồng nhau đến giá trị nhỏ nhất được cho phép theo đặc tả HTML5: 4 ms (thay vì 10 ms được sử dụng để kẹp).


1
Điều này rất tốt để biết - ví dụ: đối với các cập nhật ajax trong đó dữ liệu mới nhất luôn luôn chính xác - nhưng đối với câu hỏi được đăng không có nghĩa là hoạt hình bị chậm / tạm dừng đáng kể vì "a" sẽ không được tăng số lần thích hợp ?

5

Tôi nghĩ rằng một sự hiểu biết tốt nhất về vấn đề này là trong ví dụ này: http://jsfiddle.net/TAHDb/

Tôi đang làm một điều đơn giản ở đây:

Có một khoảng thời gian là 1 giây và mỗi lần ẩn nhịp đầu tiên và di chuyển nó đến lần cuối và hiển thị nhịp thứ 2.

Nếu bạn ở lại trên trang nó hoạt động như nó được cho là. Nhưng nếu bạn ẩn tab trong vài giây, khi bạn quay lại, bạn sẽ thấy một điều mệt mỏi.

Nó giống như tất cả các sự kiện không xảy ra trong thời gian bạn không hoạt động bây giờ sẽ kéo dài tất cả trong 1 lần. vì vậy trong vài giây bạn sẽ nhận được như các sự kiện X. chúng nhanh đến mức có thể nhìn thấy tất cả 6 nhịp cùng một lúc.

Vì vậy, đường nối chrome chỉ trì hoãn các sự kiện, vì vậy khi bạn quay lại, tất cả các sự kiện sẽ xảy ra nhưng tất cả cùng một lúc ...

Một ứng dụng pratical là ocur này cho một slideshow đơn giản. Hãy tưởng tượng các con số là Hình ảnh và nếu người dùng ở lại với tab bị ẩn khi anh ta quay lại, anh ta sẽ thấy tất cả các hình ảnh nổi, Hoàn toàn bị bỏ qua.

Để khắc phục điều này, hãy sử dụng điểm dừng (đúng, đúng) như pimvdb đã nói. Điều này sẽ xóa hàng đợi sự kiện.


4
Trong thực tế, điều này là do requestAnimationFramejQuery được sử dụng. Vì một số quirks nó có (như cái này), họ đã loại bỏ nó. Trong 1.7 bạn không thấy hành vi này. jsfiddle.net/TAHDb/1 . Bởi vì khoảng thời gian là một lần mỗi giây, điều này thực sự không ảnh hưởng đến vấn đề tôi đã đăng, vì mức tối đa cho các tab không hoạt động cũng là một lần mỗi giây - do đó không có sự khác biệt.
pimvdb

4

Đối với tôi không quan trọng để phát âm thanh ở chế độ nền như đối với những người khác ở đây, vấn đề của tôi là tôi có một số hình ảnh động và chúng hoạt động như điên khi bạn ở trong các tab khác và quay lại với chúng. Giải pháp của tôi là đặt những hình ảnh động này vào bên trong nếu điều đó ngăn tab không hoạt động :

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

nhờ đó hoạt hình của tôi chỉ chạy nếu tab hoạt động. Tôi hy vọng điều này sẽ giúp ai đó với trường hợp của tôi.


1
Tôi sử dụng nó như thế này setInterval(() => !document.hidden && myfunc(), 1000)và nó hoạt động hoàn hảo
Didi Bear

4

Cả hai setIntervalrequestAnimationFramekhông hoạt động khi tab không hoạt động hoặc hoạt động nhưng không đúng thời gian. Một giải pháp là sử dụng một nguồn khác cho các sự kiện thời gian. Ví dụ: ổ cắm web hoặc nhân viên web là hai nguồn sự kiện hoạt động tốt trong khi tab không hoạt động. Vì vậy, không cần phải chuyển tất cả mã của bạn sang một nhân viên web, chỉ cần sử dụng worker làm nguồn sự kiện thời gian:

// 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;
}

liên kết jsfiddle của mẫu này.


"chỉ sử dụng worker làm nguồn sự kiện thời gian" Cảm ơn ý tưởng
Promod Sampath Elvitigala

1

Bị ảnh hưởng nặng nề bởi thư viện của Ruslan Tushov, tôi đã tạo ra thư viện nhỏ của riêng mình . Chỉ cần thêm tập lệnh vào <head>và nó sẽ vá setIntervalsetTimeoutvới những người sử dụng WebWorker.


chỉ thử nó trên chrome 60 nhưng có vẻ như không có tác dụng gì cả chuyển đổi tab (giống như bình thường không có plugin)
neoDev

Tôi sử dụng nó với Chrome 60. Bạn có thể tạo bản demo và đăng nó lên các vấn đề về github không?
Mariy

1

Phát tệp âm thanh đảm bảo hiệu suất Javascript nền đầy đủ trong thời gian này

Đối với tôi, đó là giải pháp đơn giản và ít xâm phạm nhất - ngoài việc phát ra âm thanh mờ / gần như trống rỗng, không có tác dụng phụ tiềm năng nào khác

Bạn có thể tìm thấy thông tin chi tiết tại đây: https://stackoverflow.com/a/51191818/914546

(Từ các câu trả lời khác, tôi thấy rằng một số người sử dụng các thuộc tính khác nhau của thẻ Audio, tôi tự hỏi liệu có thể sử dụng thẻ Audio để có hiệu suất đầy đủ mà không thực sự phát một cái gì đó không)


0

Đây là giải pháp thô của tôi

(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;
})();

postMessageHandlergọi sự kiện của chính nó mà nó đang xử lý, do đó có một vòng lặp vô hạn không chặn UI. Và trong khi nó lặp vô hạn, nó đang kiểm tra với mỗi xử lý sự kiện nếu có chức năng thời gian chờ hoặc khoảng thời gian để chạy.
foobored

0

Lưu ý: giải pháp này không phù hợp nếu bạn thích khoảng thời gian của bạn hoạt động trên nền, ví dụ: phát âm thanh hoặc ... nhưng nếu bạn bối rối chẳng hạn về hoạt hình của bạn không hoạt động đúng khi quay lại trang (tab) thì đây là một giải pháp tốt.

Có nhiều cách để đạt được mục tiêu này, có lẽ "WebWorkers" là cách tiêu chuẩn nhất nhưng chắc chắn, đó không phải là cách dễ nhất và tiện dụng, đặc biệt là nếu bạn không có đủ Thời gian, vì vậy bạn có thể thử theo cách này:

► KHÁI NIỆM:

1- xây dựng tên cho khoảng thời gian của bạn (hoặc hoạt hình) và đặt khoảng thời gian của bạn (hoạt hình), để nó sẽ chạy khi người dùng lần đầu mở trang của bạn: var interval_id = setInterval(your_func, 3000);

2- bởi $(window).focus(function() {});$(window).blur(function() {});bạn có thể tắt clearInterval(interval_id)trình duyệt (tab) mọi lúc và ReRun khoảng thời gian của bạn (hoạt hình) mỗi lần trình duyệt (tab) sẽ hoạt động trở lại bởiinterval_id = setInterval();

► MÃ SỐ:

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;
});

0

Tôi nghĩ rằng cách dễ nhất để có được điều này bây giờ jQuery 3.3.xlà sử dụng chức năng này:

intervalFunction();

$([window, document]).on('focusin', function(){
    if (!intervalId){
        intervalFunction();
    }
}).on('focusout', function(){
    if (intervalId) {
        clearInterval(intervalId);
        intervalId = undefined;
    }
});

function intervalFunction() {
    // Your function hire
}

intervalFunction() là init ngay lập tức khi tải trang, và sau đó nếu tiêu điểm của bạn quay lại, bạn cố gắng kích hoạt lại khoảng thời gian, nhưng nếu trang của bạn mất tiêu điểm, bạn luôn có thể quay lại điều này.

Thật rõ ràng và dễ dàng. Ngoài ra, các giải pháp này không sử dụng chức năng không dùng nữa như .focus(), .focusin().focusout()


0

Đó là một câu hỏi khá cũ nhưng tôi gặp phải vấn đề tương tự.
Nếu bạn chạy web của mình trên chrome, bạn có thể đọc qua bài viết này Tab nền trong Chrome 57 .

Về cơ bản bộ định thời gian có thể chạy nếu nó không hết ngân sách hẹn giờ.
Việc tiêu thụ ngân sách dựa trên thời gian sử dụng CPU của tác vụ bên trong bộ đếm thời gian.
Dựa trên kịch bản của tôi, tôi vẽ video lên khung vẽ và chuyển sang WebRTC.
Kết nối video webrtc sẽ tiếp tục cập nhật ngay cả khi tab không hoạt động.

Tuy nhiên bạn phải sử dụng setIntervalthay vì requestAnimationFrame.
nó không được khuyến khích để kết xuất UI mặc dù.

Sẽ là tốt hơn để lắng nghe visibilityChangesự kiện và thay đổi kết xuất cơ học phù hợp.

Ngoài ra, bạn có thể thử @ kaan-soral và nó sẽ hoạt động dựa trên tài liệu.


-1

Tôi đã có thể gọi chức năng gọi lại của mình tối thiểu 250ms bằng cách sử dụng thẻ âm thanh và xử lý sự kiện ontimeupdate của nó. Nó được gọi 3-4 lần trong một giây. Nó tốt hơn một giây setTimeout trễ

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.