2016-08-15 16 views
3

setTimeout(func, 0)が呼び出された関数に大きな遅延を引き起こす原因を知っている人はいますか? setTimeout(func, 0)が実行されてから実際にfuncが呼び出されるまでの間に、1.2秒以上の遅延が見られます。setTimeout(コールバック、0)からのコールバックの遅延の原因

私はこの動作をChrome v52でのみ表示します(v51にはこの機能がありません)。だから、Chromeの問題かもしれませんが、バグとして報告する前に、私はそれを調査したいと思います。

私は残念ながらそれをreprosする小さなケースを持っていません。これは、スクロールイベントに応答してAngularJSアプリで発生します。マウスホイールでスクロールイベントトリガーを処理します。その一部として、setTimeout(func, 0)を呼び出してフォローアップ作業を行います。

Chrome devtoolsを使用すると、この1.2秒のギャップの間に実行されるjavascriptがほとんどないことがわかります。私はスレッドをブロックしていません。実際には、console.profileとconsole.profileEndを使用して、特定の1.2秒間のgrapをプロファイルしました。その時間の99%がアイドルであることを示しています。

devtoolsでタイムラインを見ると、その期間中に「マウスホイール」イベントが処理されることがわかります。それらの多くがあります(ギャップの最初の1秒間は5ミリ秒毎に1つです)。マウスホイールがその時間の間にスクロールされていないので、これは少し奇妙です。私はMac Magic Mouseの仮想スクロールホイールを1回フリックし、スクロール可能な領域の上部に叩きつけます。イベントが終了した後、マウスホイールイベントが発生し続けます。

誰にもアイデアはありますか?非常に速く実行されるマウスホイールイベントの小さなストリームがコールバックの呼び出しを遅らせることができますか?そのタイムアウトコールバックがその予定時刻の1.2秒後に呼び出された理由を調査する方法はありますか?

:setTimeoutのショーコールバックを表すタイムラインの画像が追加されました。画像

のsetTimeout(FUNC、0)タスクに並んイベントがすでに存在する場合、あなたのコードが第二の0で、実行されます保証するものではありませんを含めるためにここに投稿

Timeline of slow setTimeout

+1

バグとして報告するには、再現可能な例が必要です。だから、躊躇せずに投稿してください。 – estus

+0

'setTimeout(func、0)'は、関数をキューに入れ、次のイベントループと呼ばれるようにスケジューリングします。つまり、次の "tick"と言います。ブラウザはUI用のスレッドを1つしか使用しないため、何か問題が発生する可能性があります。マウスのホイールイベントは関連している可能性がありますが、何や理由があるかを調べることなく明確な方法があるとは思いません。 – vlaz

+0

これが起こったときにタイムラインを示す画像を追加しました。コンソールは 'setTimeout'メソッドが呼び出されたときとコールバックが実行されたときにログを記録します。この例では、1.4秒以上かかる。要約グラフは、大部分の時間、アイドル状態であることを示しています。それは本当に誰かがスレッドをホギングしていることができますか? – Steven

答えて

-1

(おそらくスクロールによってトリガされる)、それらは実行スタックに入れられ実行されなければならない。 https://www.youtube.com/watch?v=8aGhZQkoFbQ

event loop and task queue

から

チェックこの画像を使用すると、即時実行が必要な場合、あなたは何のイベントリスナを意味していない、何のイベントは、ページをスクロールしている時にタスクキューに追加されていないことを確認する必要があります.on('scroll', func)のようにするか、funcを登録する前にsetTimeoutを実行してください。.on('scroll')

+0

私は、0秒で動作することが保証されていないことを非常に理解していますが、1.2秒後よりはるかに早く動作することを願っています。 元の投稿に、発生したイベントのタイムラインを示す画像を追加しました。タイムラインにsetTimeoutコールを実行した時刻とコールバックを取得した時刻をマークするコンソール出力を追加しました。この例では、1.4秒以上かかりました。 要約グラフには、コールバックを待っている時間のほとんどがアイドル状態であることも示されています。だから、それは本当に私たちを妨害する別の出来事かもしれませんか? – Steven

+0

なぜ落札したのか分かりませんが、「非常に速く実行されるマウスホイールイベントの小さなストリームがコールバックの呼び出しを遅らせることができますか?答えはほとんどの場合、オンスクロールのコールバックが何をしているのかによって異なります。 – David

1

setTimeout(..., 0)はイベントをイベントキューに入れます。しかし、既にキューに入れられている他のイベントより優先されるわけではありません。確かにスクロールイベントを処理するときは、多くのスクロールイベントがキューに入れられる状況になりますが、スクロールイベントハンドラコードは以前のスクロールイベントを処理しています。

実際、スクロールイベントを処理するときには、簡単に遅延が発生します。コールスタックが空の場合、つまり現在実行中のイベントハンドラが終了すると、次のイベントがイベントキューから取得されます。そのキューのどこかにタイムアウト関連のイベントがあっても、優先順位は高くなりません。すべてのイベントは順番を待たなければなりません。

たとえば、キュ​​ー内に既に10個のスクロールイベントがある場合、それらはすべて処理されなければならず、マウスがまだスクロールしているときは、問題が悪化するだけです。より高いレートでコードが処理できるようにすることができます。

これを解決する1つの方法は、スクロールイベントハンドラをできるだけ軽くし、重いものを非同期的に処理することです。ハンドラが以前の実行で既に何らかの方法で実行がスケジュールされていることを検出した場合、そのタスクをキャンセルして新しいバージョンに置き換えることができます。

サンプルコード:

var task = -1; 
window.addEventListener('scroll', function(e) { 
    clearTimeout(task); 
    task = setTimeout(function() { 
     // all the (heavy) processing related to scrolling comes here. 
    }, 0); 
}; 

このsetTimeoutは質問から1と混同してはなりません。しかし、別のsetTimeoutを開始した場合、それは順番に処理されることに注意してください。上記のコードは、実際の処理をキューの最後に移動するだけです。しかし、さらに多くのスクロールイベントがある場合は、それぞれキューからタイムアウトイベントを削除し、キューのエンドに新しいキューを入れます。

これは、実際にはスクロールハンドラコードの重い部分の実行が少なくなるため、一部のアニメーションの流動性にいくつかの悪影響を及ぼします。しかし、が遅れてになるのを防ぎ、ボーナスとして、あなたの他のsetTimeoutイベントが早くターンします。

+0

* "... 1つの[スクロールイベント]間隔の約1秒間は5ミリ秒ごとに..." *:200スクロールイベント1秒で* "この時間にマウスホイールがスクロールされていない*":これは、開発ツールが何を言っていても、JavaScriptがイベントキューに追いつかないことを意味します。 – trincot

+0

これはファンクションコールの「デバウンス」とも呼ばれます。 https://davidwalsh.name/javascript-debounce-functionも参照してください。 –

関連する問題