Chromeでタブが非アクティブのときにsetIntervalを機能させるにはどうすればよいですか?


189

1 setInterval秒あたり30回のコードを実行しています。これはうまく機能しますが、別のタブを選択すると(コードが含まれているタブが非アクティブになるため)、setInterval何らかの理由でタブがアイドル状態に設定されます。

私はこの簡略化したテストケースを作成しました(http://jsfiddle.net/7f6DX/3/):

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

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

このコードを実行してから別のタブに切り替え、数秒待ってから戻ると、アニメーションは他のタブに切り替えたときの時点から続きます。したがって、タブがアクティブでない場合、アニメーションは1秒に30回実行されません。これは、setInterval関数が毎秒呼び出される回数を数えることで確認できます。これは30ではなく、タブがアクティブでない場合は1または2になります。

これはパフォーマンスを向上させるために設計によって行われたと思いますが、この動作を無効にする方法はありますか?それは実際私のシナリオでは不利です。


3
Dateオブジェクトと一緒にハッキングして、実際に経過した時間を確認しない限り、おそらくそうではありません。
アレックス、

シナリオについて詳しく説明していただけますか?多分それはそのような欠点ではありません。
James

2
あなたはこのコードの変更を意味し、あなたが尋ねたこともここで議論されます -Oliver Mattosはいくつかの回避策を投稿しました、おそらくあなたのケースでも有効ですか?
シャドウウィザードは、

6
から読み取ったように、アニメーションの開始以降に経過したリアルタイムの時間を基にアニメーションにキーを設定することは、ほとんどの場合最善Dateです。そのため、間隔がすばやく起動しない場合(この理由またはその他の理由により)、アニメーションはジャーキーになり、遅くなることはありません。
ボビンス、2011年

1
ここで質問のタイトルが私を導きましたが、私のユースケースは少し異なりました-タブの非アクティブに関係なく認証トークンを更新する必要がありました。それがあなたに関連しているなら、ここで
Erez Cohen

回答:


152

ほとんどのブラウザーでは、非アクティブなタブの実行の優先度が低く、これがJavaScriptタイマーに影響を与える可能性があります。

トランジションの値が、間隔ごとに固定された増分ではなく、フレーム間のリアルタイム経過時間を使用して計算された場合、この問題を回避するだけでなく、プロセッサーが使用されていない場合に最大60 fpsを取得できるため、requestAnimationFrameを使用することでアニメーションをスムーズにすることができます。とても忙しい。

これは、以下を使用したアニメーション化されたプロパティ遷移の一般的な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ワーカーを使用できます。見てみましょう以下Möhreの回答詳細については...

CSS対JSの「アニメーション」

この問題や他の多くの問題は、JavaScriptベースのアニメーションの代わりにCSSトランジション/アニメーションを使用することで回避でき、かなりのオーバーヘッドが追加されます。メソッドと同じようにCSSトランジションを利用できるこのjQueryプラグインをお勧めしanimate()ます。


1
FireFox 5でこれを試しましたが、タブがフォーカスされていなくても「setInterva」が実行されます。「setInterval」の私のコードは、「animate()」を使用してスライドショーをスライドさせます。アニメーションはFireFoxによってキューに入れられているようです。タブに戻ると、FireFoxが次々にキューをポップし、キューが空になるまでスライドショーが高速で移動します。
nthpixel 2011

@nthpixelは、(前のアニメーションをクリアするたびに)tatの状況で.stop(wwwの回答に従って)ヘルプを追加します

@gordatron-.stopは私の状況を助けません。同じ動作。そして今、FireFox 6.0.2を使用しています。それはjQueryが.animateを実装する方法なのだろうか。
nthpixel 2011

@cvsguimaraes-はい、良い点です。バックグラウンドタブでもWebワーカーが実行されることを保証する仕様について何か知っていますか?私は、ブラウザベンダーが将来的にもこれらをクランプすることを任意に決定することを少し心配しています...
UpTheCreek

前の呼び出しの最後ではなく開始before = now;からの経過時間を取得するために、コードの次の最後の行を読み取るように変更します。これは通常、アニメーションを作成するときに必要です。現在のように、間隔関数の実行に15ミリ秒かかる場合、次に呼び出されるとき(間隔が50ミリ秒ではなく)に35ミリ秒が与えられます(タブがアクティブなとき)。elapsedTime
ジョナスベルリン

71

オーディオフェージングとHTML5プレーヤーで同じ問題が発生しました。タブが非アクティブになるとスタックしました。そのため、WebWorkerが制限なしに間隔/タイムアウトを使用できることがわかりました。「ティック」をメインの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;
    }
});

6
きちんと!今度は彼らがこれを「修正」しないことを望みましょう。
pimvdb 2012

2
すごい!このアプローチは、タイマーを動作させるだけでなく、アニメーションの問題を修正する必要がある場合にのみ使用してください。
Konstantin Smolyanin 2013

1
これで素晴らしいメトロノームを作りました。最も興味深いのは、最も一般的なすべてのhtml5ドラムマシンがこのメソッドを使用せず、代わりに劣った通常のsetTimeoutを使用することです。skyl.github.io/grimoire/fretboard-prototype.html-現在、ChromeまたはSafariが最適です。
Skylar Saveland 2013

開発者が悪用し始めれば、彼らはそれを「修正」します。まれな例外を除いて、ユーザーエクスペリエンスの改善を最小限に抑えるためにバックグラウンドでリソースを消費することは、アプリにとってそれほど重要ではありません。
ガンチャーズ

41

Webワーカーを使用する解決策があります(前述のとおり)。これらは別々のプロセスで実行され、速度が低下しないためです。

コードを変更せずに使用できる小さなスクリプトを作成しました-それは単に関数setTimeout、clearTimeout、setInterval、clearIntervalをオーバーライドします。

すべてのコードの前に含めるだけです。

詳細はこちら


1
タイマーを使用していたライブラリを含むプロジェクトでテストしました(そのため、自分でワーカーを実装することはできませんでした)。それは魅力のように働きました。このライブラリをありがとう
ArnaudR

@ ruslan-tushovがchrome 60で試したところ、まったく効果がないようです... cssアニメーションによってアニメーション化されている要素をDOMに追加/削除するために、いくつかのsetTimeoutからいくつかの関数を呼び出しています。タブ切り替え後の遅延あり(プラグインなしで通常どおり)
neoDev 2017

@neoDev他のJavaScriptより先にHackTimer.js(またはHackTimer.min.js)をロードしますか?
Davide Patti

@neoDev最近試してみたので、本当に奇妙に聞こえます
Davide Patti

@DurdenPはい、他のJavaScriptよりも前に配置しようとしました。たぶん私はCSSアニメーションを使っていたのでしょうか?Webワーカーも試しましたが、運が悪かったのを覚えています
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秒あたり1回のタイムアウトのみを送信するようにクランプします。さらに、ネストされたタイムアウトを、HTML5仕様で許可されている最小値である4ミリ秒にクランプするようになりました(クランプに使用されていた10ミリ秒ではなく)。


1
これは知っておくと便利です。たとえば、最新のデータが常に正しいajax更新の場合-投稿された質問は、「a」が適切な回数インクリメントされなかったためにアニメーションが大幅に遅くなったり一時停止したりすることを意味しません?

5

この問題についての最もよい理解は、この例にあると思います:http : //jsfiddle.net/TAHDb/

私はここで簡単なことをしています:

1秒の間隔をあけ、毎回最初のスパンを非表示にして最後に移動し、2番目のスパンを表示します。

ページに留まる場合は、想定どおりに機能します。しかし、数秒間タブを非表示にすると、戻ったときに奇妙なものが表示されます。

これは、非アクティブな時間に発生しなかったすべてのイベントと同様に、一度にすべて発生します。したがって、数秒間、Xイベントのようになります。それらは非常に高速なので、6つのスパンすべてを一度に表示できます。

したがって、クロムを継ぎ合わせてイベントを遅延させるだけなので、戻ったときにすべてのイベントが発生しますが、一度にすべて...

実用的なアプリケーションは、この簡単なスライドショーの場合です。数字が画像であることを想像してみてください。ユーザーが戻ってきたときにタブを非表示のままにしておくと、すべてのimgが浮いているのがわかります。

これを修正するには、pimvdbが言うように、stop(true、true)を使用します。これはイベントキューをクリアします。


4
実際、これはrequestAnimationFramejQueryが使用されているためです。(このように)いくつかの癖のため、それらを削除しました。1.7では、この動作は見られません。jsfiddle.net/TAHDb/1。間隔は1秒に1回なので、非アクティブなタブの最大数も1秒に1回であるため、これは実際には投稿した問題に影響しません。
pimvdb

4

私にとっては、他の人のようにバックグラウンドでオーディオを再生することは重要ではありません。私の問題は、いくつかのアニメーションがあり、他のタブにいて戻ってきたときにそれらが狂ったように動作することでした。私の解決策は、非アクティブなタブを防ぐことができる場合にこれらのアニメーションを中に入れることでした:

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

そのおかげで、私のアニメーションはタブがアクティブな場合にのみ実行されていました。これが私のケースで誰かを助けることを願っています。


1
私はこれをこのように使用し、setInterval(() => !document.hidden && myfunc(), 1000)完全に機能します
Didi Bear

4

両方ともsetIntervalrequestAnimationFrameタブが非アクティブまたは機能しているが適切な期間では機能していない場合は機能しません。解決策は、タイムイベントの別のソースを使用することです。たとえば、WebソケットまたはWebワーカーは、タブがアクティブでないときに正常に動作する2つのイベントソースです。したがって、すべてのコードをWebワーカーに移動する必要はありません。ワーカーをタイムイベントソースとして使用するだけです。

// 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リンク


「単にタイムイベントソースとしてワーカーを使用する」アイデアをありがとう
プロモサンパスElvitigala

1

Ruslan Tushovのライブラリの影響を強く受けて、私は自分の小さなライブラリを作成しました。にスクリプトを追加するだけで、<head>パッチが適用されsetIntervalsetTimeoutを使用するスクリプトが追加されますWebWorker


クロム60で試しただけですが、まったく効果がないようです... cssアニメーションによってアニメーション化される要素をDOMに追加/削除するために、いくつかのsetTimeoutからいくつかの関数を呼び出していますが、しばらくするとフリーズしますタブスイッチ(プラグインなしで通常どおり)
neoDev

Chrome 60で使用しています。デモを作成してgithubの問題に投稿できますか?
Mariy 2017

1

オーディオファイルを再生すると、当面の間、完全なバックグラウンドJavaScriptパフォーマンスが保証されます

私にとって、これは最もシンプルで邪魔にならない解決策でした-かすかな/ほとんど空のサウンドを再生することを除いて、他の潜在的な副作用はありません

あなたはここで詳細を見つけることができます: https //stackoverflow.com/a/51191818/914546

(他の回答から、一部の人々はAudioタグのさまざまなプロパティを使用していることがわかります。実際に何かを再生せずに、Audioタグを完全なパフォーマンスに使用できるかどうか疑問に思います)


0

これが私の大まかな解決策です

(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をブロックしていない無限ループを有し、それは処理されている独自のイベントを呼び出します。そして、それは無限にループしていますが、実行するタイムアウトまたはインターバル関数があるかどうかを各イベント処理でチェックしています。
18年

0

注:この解決策は、オーディオの再生など、間隔がバックグラウンドで機能する場合は適していませんが、ページ(タブ)に戻ったときにアニメーションが適切に機能しないなどの混乱がある場合、これは良い解決策です。

この目標を達成するには多くの方法があります。おそらく「WebWorkers」が最も標準的な方法ですが、確かに、これは最も簡単で便利な方法ではありません。特に十分な時間がない場合は、次のように試すことができます。

►基本概念:

1-間隔(またはアニメーション)の名前を作成し、間隔(アニメーション)を設定して、ユーザーが初めてページを開いたときに実行されるようにします。 var interval_id = setInterval(your_func, 3000);

2 -で$(window).focus(function() {});$(window).blur(function() {});あなたがすることができclearInterval(interval_id)、あなたの間隔(アニメーション)毎回ブラウザ(タブ)毎回ブラウザ(タブ)がdeactivedされ、再実行によって再びaciveう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

これを今すぐ取得する最も簡単な方法jQuery 3.3.xは、この関数を使用することだと思います。

intervalFunction();

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

function intervalFunction() {
    // Your function hire
}

intervalFunction() はページの読み込み時に即座に初期化され、フォーカスが戻った場合は間隔を再アクティブ化しようとしますが、ページがフォーカスを失った場合はいつでもこれに戻ることができます。

それは明確で簡単です。さらに、このソリューションでは.focus().focusin()などの廃止された関数を使用していません。.focusout()


0

かなり古い質問ですが、同じ問題が発生しました。Chromeで
Webを実行している場合は、この投稿の「Chrome 57のバックグラウンドタブ 」を読むことができます。

基本的に、インターバルタイマーは、タイマーバジェットを使い果たしていない場合に実行できます。
予算の消費は、タイマー内のタスクのCPU時間の使用に基づいています。
私のシナリオに基づいて、ビデオをキャンバスに描画し、WebRTCに転送します。
タブがアクティブでない場合でも、webrtcビデオ接続は更新を続けます。

ただし、のsetInterval代わりに使用する必要がありrequestAnimationFrameます。
ただし、UIレンダリングにはお勧めしません。

visibilityChangeイベントをリッスンし、それに応じてレンダリングメカニズムを変更することをお勧めします。

さらに、@ kaan-soralを試してみると、ドキュメントに基づいて機能するはずです。


-1

オーディオタグを使用し、そのontimeupdateイベントを処理することで、最低250ミリ秒でコールバック関数を呼び出すことができました。1秒間に3〜4回呼び出されます。1秒遅れるsetTimeoutより良い

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.