Chrome에서 탭이 비활성 상태 일 때 setInterval도 작동하게하려면 어떻게해야하나요?


190

나는 한 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함수가 1 초마다 호출 되는 횟수를 세어 확인할 수 있습니다 . 탭이 비활성화되어 있으면 30이 아니라 1 또는 2입니다.

나는 이것이 성능을 향상시키기 위해 의도적으로 설계된 것이라고 생각하지만이 동작을 비활성화하는 방법이 있습니까? 내 시나리오에서는 실제로 단점입니다.


3
Date시간이 지 났는지 실제로보기 위해 객체 와 함께 해킹하지 않는 한 아마도 아닐 것 입니다.
alex

시나리오에 대해 더 자세히 설명해 주시겠습니까? 어쩌면 그런 단점이 아닙니다.
James

2
코드 변경을 의미 하고 요청한 내용도 여기에서 논의 됩니다 . Oliver Mattos가 몇 가지 해결 방법을 게시했습니다. 아마도 귀하의 경우에도 유효합니까?
Shadow Wizard is Ear For You

6
에서 읽은 것처럼 애니메이션을 시작한 이후에 경과 한 실시간 양을 애니메이션으로 표시하는 것이 가장 좋습니다 Date. 따라서 인터벌이 빠르게 또는 발사되지 않는 경우 (이러한 이유로 또는 다른 이유로) 애니메이션이 느리게 진행되지 않습니다.
bobince

1
질문의 제목으로 인해 여기에 나에게 도움이되었지만 유스 케이스는 약간 달랐습니다. 탭의 비활성에 관계없이 인증 토큰을 새로 고쳐야했습니다. 그것이 당신과 관련이 있다면 내 대답을 확인 하십시오
Erez Cohen

답변:


153

대부분의 브라우저에서 비활성 탭은 우선 순위가 낮으므로 JavaScript 타이머에 영향을 줄 수 있습니다.

각 간격마다 고정 증분 대신 프레임간에 실시간 경과 시간을 사용하여 전환 값을 계산 경우이 문제를 해결할 수있을뿐만 아니라 프로세서가없는 경우 최대 60fps를 얻을 수 있으므로 requestAnimationFrame 을 사용하여 smother 애니메이션을 얻을 수도 있습니다. 아주 바쁜.

다음은 다음을 사용하는 애니메이션 속성 전환의 바닐라 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 웹 노동자 . 한 번 봐 가지고 아래 Möhre의 대답 자세한 내용을 ...

CSS 대 JS "애니메이션"

상당한 오버 헤드를 추가하는 JavaScript 기반 애니메이션 대신 CSS 전환 / 애니메이션을 사용하면 이 문제와 다른 많은 문제를 피할 수 있습니다. 메소드 와 마찬가지로 CSS 전환을 활용할 수있는 이 jQuery 플러그인 을 권장 animate()합니다.


1
FireFox 5에서 이것을 시도했지만 탭에 초점이 맞지 않아도 'setInterva'l이 계속 실행됩니다. 'setInterval'의 내 코드는 'animate ()'를 사용하여 슬라이드 쇼를 슬라이드합니다. FireFox가 애니메이션을 큐에 넣은 것 같습니다. 탭으로 돌아 가면 FireFox가 대기열을 하나씩 차례로 팝업하여 대기열이 비워 질 때까지 빠르게 움직이는 슬라이드 쇼를 만듭니다.
nthpixel

@nthpixel은 tat 상황에서 (www의 답변에 따라) .stop 도움말을 추가 할 것입니다 (이전 애니메이션을

@ gordatron-.stop은 내 상황에 도움이되지 않습니다. 같은 행동입니다. 그리고 지금 FireFox 6.0.2를 사용하고 있습니다. jQuery가 .animate를 구현하는 방식인지 궁금합니다.
nthpixel

@ cvsguimaraes-좋은 지적입니다. 백그라운드 탭에서도 웹 워커가 실행되도록 보장하는 사양에 대해 알고 있습니까? 나는 브라우저 공급 업체들이 미래에도 이것들을 임의로 결정하기로 결정 할까 걱정하고 있습니다.
UpTheCreek

이전 호출 의 대신 시작before = now; 이후 경과 시간을 얻기 위해 읽을 코드의 마지막 줄을 변경합니다 . 일반적으로 사물을 애니메이션 할 때 원하는 것입니다. 현재와 ​​같이 interval 함수 실행에 15ms 가 걸리면 다음에 호출 될 때 (탭이 활성화 된 경우) 35ms (간격 인 50ms 대신)가됩니다. elapsedTime
조나스 베를린

71

오디오 페이딩 및 HTML5 플레이어와 동일한 문제가 발생했습니다. 탭이 비활성화되면 멈추었습니다. 그래서 WebWorker가 간격 / 시간 초과를 제한없이 사용할 수 있음을 알았습니다. "틱"을 기본 자바 스크립트에 게시하는 데 사용합니다.

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

주요 자바 스크립트 :

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
산뜻한! 이제 그들이 이것을 "수정"하지 않기를 바랍니다.
pimvdb

2
큰! 이 방법은 타이머가 작동해야하지만 애니메이션 문제를 해결하기 위해서가 아니라면 사용해야합니다!
Konstantin Smolyanin

1
나는 이것으로 큰 메트로놈을 만들었습니다. 가장 인기있는 모든 html5 드럼 머신이이 방법을 사용하지 않고 대신 정기적 인 열등한 setTimeout을 사용한다는 점에 흥미가 있습니다. skyl.github.io/grimoire/fretboard-prototype.html- 크롬 또는 사파리가 지금 최고를 재생합니다.
Skylar Saveland

개발자가 악용하기 시작하면 "수정"합니다. 드문 경우를 제외하고 앱은 사용자 환경에서 최소한의 개선을 제공하기 위해 백그라운드에서 리소스를 소비해야하는 것이 중요하지 않습니다.
Gunchars

41

웹 작업자는 별도의 프로세스에서 실행되며 속도가 느려지지 않기 때문에 (앞서 언급했듯이) 사용하는 솔루션이 있습니다.

코드를 변경하지 않고 사용할 수있는 작은 스크립트를 작성했습니다. setTimeout, clearTimeout, setInterval, clearInterval 함수를 무시합니다.

모든 코드 앞에 포함하십시오.

더 많은 정보는 여기에


1
타이머를 사용하는 라이브러리를 포함한 프로젝트에서 테스트 했으므로 작업자를 직접 구현할 수 없었습니다. 그것은 매력처럼 작동했습니다. 이 라이브러리에 감사드립니다
ArnaudR

@ ruslan-tushov는 크롬 60에서 그것을 시도했지만 전혀 효과가없는 것 같습니다 ... CSS 애니메이션에 의해 애니메이션되는 DOM에 요소를 추가 / 제거하기 위해 여러 setTimeout에서 몇 가지 함수를 호출하고 있습니다. 탭 스위치 후 약간의 지연 (플러그인없이 평소와 같이)
neoDev

@neoDev 다른 JavaScript보다 HackTimer.js (또는 HackTimer.min.js)를로드합니까?
Davide Patti

@neoDev 최근에 시도한 후 작동하므로 정말 이상하게 들립니다
Davide Patti

@DurdenP 예, 다른 JavaScript보다 먼저 넣으려고했습니다. CSS 애니메이션을 사용했기 때문일 수 있습니다. 또한 웹 워커도 시도했지만 운이없는 것을 기억합니다.
neoDev

13

그냥 이렇게 :

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

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

비활성 브라우저 탭은 일부 setInterval또는 setTimeout기능을 버퍼링 합니다.

stop(true,true) 버퍼링 된 모든 이벤트를 중지하고 마지막 애니메이션 만 즉시 실행합니다.

window.setTimeout()방법은 비활성 탭에서 초당 최대 1 개의 타임 아웃을 보내도록 고정됩니다. 또한 이제 중첩 시간 초과를 HTML5 사양에서 허용하는 가장 작은 값인 4ms (클램핑하는 데 사용 된 10ms 대신)로 고정합니다.


1
예를 들어 최신 데이터가 항상 정확한 아약스 업데이트의 경우 알 수 있습니다. 그러나 게시 된 질문에 대해 "a"가 적절한 횟수만큼 증가하지 않았기 때문에 애니메이션이 크게 느려지거나 일시 중지 된 것은 아닙니다. ?

5

이 문제에 대한 최선의 이해는이 예제에 있다고 생각합니다. http://jsfiddle.net/TAHDb/

나는 여기서 간단한 일을하고있다 :

1 초 간격을두고 매번 첫 번째 스팬을 숨기고 마지막으로 이동하여 두 번째 스팬을 표시하십시오.

페이지에 머무르면 예상대로 작동합니다. 그러나 몇 초 동안 탭을 숨기면 돌아올 때 위조 된 것이 나타납니다.

비활성화 된 시간 동안 발생하지 않은 모든 이벤트는 이제 한 번만 발생합니다. 몇 초 동안 X 이벤트처럼 보일 것입니다. 6 개 범위를 한 번에 모두 볼 수있을 정도로 빠릅니다.

그래서 이음새 크롬은 이벤트 만 지연하므로 모든 이벤트가 다시 발생하면 한 번에 모두 발생합니다 ...

실용적인 응용 프로그램은 간단한 슬라이드 쇼를위한이 문제였습니다. 숫자가 이미지라고 상상해보십시오. 사용자가 돌아 왔을 때 탭이 숨겨져 있으면 모든 imgs가 떠 다니는 것을 볼 수 있습니다.

이 문제를 해결하려면 pimvdb와 같은 stop (true, true)을 사용하십시오. 이벤트 큐를 지 웁니다.


4
실제로 이것은 requestAnimationFrame사용 된 jQuery 때문입니다 . (이와 같은) 몇 가지 단점 때문에 그들은 그것을 제거했습니다. 1.7에서는이 동작이 보이지 않습니다. jsfiddle.net/TAHDb/1 . 간격은 초당 한 번이므로 비활성 탭의 최대 값도 초당 한 번이므로 차이가 발생하지 않으므로 실제로 게시 한 문제에 영향을 미치지 않습니다.
pimvdb

4

나에게 여기서 다른 사람들처럼 백그라운드에서 오디오를 재생하는 것은 중요하지 않습니다. 내 문제는 애니메이션이 있고 다른 탭에 있고 다시 돌아올 때 미친 듯이 행동했다는 것입니다. 내 탭에서 비활성 탭을 막는 경우 내 솔루션이 이러한 애니메이션을 넣었습니다 .

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

덕분에 탭이 활성화 된 경우 에만 애니메이션이 실행 되었습니다. 이것이 내 사건에 도움이되기를 바랍니다.


1
나는 이것을 이렇게 사용하고 setInterval(() => !document.hidden && myfunc(), 1000)완벽하게 작동합니다
Didi Bear

4

모두 setIntervalrequestAnimationFrame탭이 비활성 또는 작업하지만,하지 않을 권리 기간에하지 않을 때 일을. 해결책은 시간 이벤트에 다른 소스를 사용하는 것입니다. 예를 들어, 웹 소켓 또는 웹 워커 는 탭이 비활성 상태 일 때 제대로 작동하는 두 가지 이벤트 소스입니다. 따라서 모든 코드를 웹 워커로 옮길 필요가 없습니다. 워커를 시간 이벤트 소스로 사용하십시오.

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

이 샘플의 jsfiddle 링크 .


아이디어를위한 감사
Promod Sampath Elvitigala

1

Ruslan Tushov의 도서관에 큰 영향을 받아 저만의 작은 도서관을 만들었습니다 . 그냥에 스크립트를 추가 <head>하고이 패치됩니다 setIntervalsetTimeout그들 사용이와 WebWorker.


그냥 크롬 60에서 시도했지만 전혀 효과가없는 것 같습니다 ... CSS 애니메이션에 의해 애니메이션 처리되는 DOM에 요소를 추가 / 제거하기 위해 여러 setTimeout에서 몇 가지 함수를 호출하고 있습니다. 탭 스위치 (플러그인없이 평소와 같이)
neoDev

Chrome 60에서 사용합니다. 데모를 만들어 github 문제에 게시 할 수 있습니까?
Mariy

1

오디오 파일을 재생하면 당분간 전체 백그라운드 자바 스크립트 성능이 보장됩니다.

저에게있어 가장 단순하고 방해가되지 않는 솔루션이었습니다. 희미하고 거의 빈 소리를 재생하는 것 외에 다른 잠재적 부작용은 없습니다.

https://stackoverflow.com/a/51191818/914546 에서 세부 정보를 찾을 수 있습니다.

(다른 답변에서 일부 사람들은 오디오 태그의 다른 속성을 사용한다는 것을 알았습니다. 실제로 무언가를 연주하지 않고 전체 성능을 위해 오디오 태그를 사용할 수 있는지 궁금합니다)


0

참고 : 이 솔루션은 배경에서 오디오가 재생되는 등의 배경 작업을 좋아하는 경우 적합하지 않지만 페이지 (탭)로 돌아갈 때 애니메이션이 제대로 작동하지 않는 것에 대해 혼란 스럽다면 좋은 해결책.

이 목표를 달성하는 방법에는 여러 가지가 있습니다. 아마도 "WebWorkers"가 가장 표준적인 것입니다 그러나 가장 쉽고 편리한 방법은 아닙니다. 특히 시간이 충분하지 않은 경우 다음과 같이 시도 할 수 있습니다.

► 기본 개념 :

1- 간격 (또는 애니메이션)의 이름을 만들고 간격 (애니메이션)을 설정하여 사용자가 처음 페이지를 열 때 실행되도록하십시오. var interval_id = setInterval(your_func, 3000);

2로 $(window).focus(function() {});그리고 $(window).blur(function() {});당신은 할 수 있습니다clearInterval(interval_id) 브라우저 (탭)가 비활성화 때마다 브라우저 (탭)가 다시 활성화 때마다 간격 (애니메이션)을 다시 실행하십시오.interval_id = setInterval();

► 샘플 코드 :

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

꽤 오래된 질문이지만 같은 문제가 발생했습니다.
Chrome에서 웹을 실행하는 경우 Chrome 57 의이 게시물 배경 탭을 읽을 수 있습니다. 있습니다.

기본적으로 타이머 타이머가 부족하지 않으면 간격 타이머가 실행될 수 있습니다.
예산 소비는 타이머 내부 작업의 CPU 시간 사용량을 기준으로합니다.
내 시나리오에 따라 비디오를 캔버스로 그리고 WebRTC로 전송합니다.
탭이 비활성화되어 있어도 webrtc 비디오 연결은 계속 업데이트됩니다.

그러나 setInterval대신에 사용해야 합니다 requestAnimationFrame.
UI 렌더링에는 권장되지 않습니다.

듣는 것이 좋습니다 visibilityChange이벤트 그에 따라 렌더링 메커니즘을 변경하는 .

또한 @ kaan-soral을 시도 할 수 있으며 설명서를 기반으로 작동해야합니다.


-1

여기 내 거친 해결책이 있습니다.

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

postMessageHandler그것이하여 UI를 차단하지 무한 루프를 가지는, 처리하는 자신의 이벤트를 호출한다. 그리고 무한 반복되는 동안 각 이벤트 처리를 통해 실행 시간 초과 또는 간격 기능이 있는지 확인합니다.
foobored

-1

오디오 태그를 사용하고 ontimeupdate 이벤트를 처리하여 최소 250ms의 콜백 함수를 호출 할 수있었습니다. 그것은 1 초에 3-4 번이라고합니다. 1 초의 지연 시간보다 낫습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.