バックグラウンドタスクにWebアプリケーションのダウンタイムを使用する

私のアプリがモバイルデバイスでも60 fpsで動作するのが大好きです。 また、たとえば、開いているウィンドウや入力されたテキストをlocalstorageおよびユーザーメタデータ(登録されている場合)に保存するなど、アプリケーションの状態を保存して、閉じることで、後で同じ場所から作業を続行できるようにします。別のデバイス。



これはすばらしく、今日になってようやく一つの問題にぶつかりました。 実際には、1つのサイドメニューoffcanvasがあり、その状態(開いている/閉じている)をブラウザーとユーザーアカウントに保存したいと思っています。 これはlocalstorageとAJAXのレコードです。データベースの更新リクエストは非同期であり、常に複雑なアニメーション中に実行を試み、私からいくつかのフレームを盗みます。これは特にモバイルデバイスで顕著です。 明らかに、アプリケーションの重要な瞬間ではなく、アニメーションが完了した後にデータを保存したいのですが、どうすればいいですか?



アニメーション自体は非同期であり、 doPostponedStuffの呼び出しはアニメーションフレームの呼び出しと混合するため、 setTimeout(doPostponedStuff)によってスタックを一番上に追加しても役に立ちません。 アニメーションの継続時間をsetTimeoutの2番目のパラメーター、つまりsetTimeout(doPostponedStuff、680)としてハードコードできますか? いいえ、ありがとう、私はプログラミングの地獄で燃えたくありません。 ちょっと待ってください、でも何かを思い出させます。 リソースを集中的に使用する2つのタスクのうち、1つはセカンダリであり、プライマリを妨害するべきではありません...ああ、そうです! これは、ブラウザウィンドウのサイズを変更したりスクロールしたりするときに、 位置:絶対または位置:固定で要素の座標を計算することに非常に似ています。 このタスクに近づくのが簡単な場合、次のようになります。



$(window).resize(function(){ $target.css('top', x).css('left', y).css('width', z) });
      
      





しかし、この方法では失望します。スクロールすると、ブラウザは非常にすばやくフレームをドロップし始めます。なぜなら、onScrollイベントはほとんどすべてのスクロールされたピクセルでスローされるためです。 、およびDOMと再描画の操作は非常に高価です。 幸いなことに、この問題の解決策は長い間発明されてきました。



デバウンサー



したがって、前述のように、ソリューションはかなり前に発明されており、その名前はデバウンサーeng。Debouce )であり、各イベントではなくコールバックを呼び出すことで構成されますスクロールの完了時に呼び出され、すべてのスクロールピクセルではありません。 より具体的には、最後のイベントから指定された時間が経過した場合、デバウンサーはコールバックを呼び出します。 デバウンサーコード自体は非常にシンプルで、数行に簡単に収まります。



 function debounce(ms, cb){ var timeout = null; return function(){ if(timeout) clearTimeout(timeout); timeout = setTimeout(cb, ms); } }
      
      







次のように使用できます。



 $(window).scroll(debounce(100, repositionElement);
      
      







repositionElementは、スクロールの終了から100ミリ秒後に呼び出されるようになりました。これは、最も肯定的な方法でfpsに影響します。



重要なアプリケーションコードのデバッグ



これで十分ですが、アプリケーションのダウンタイムを特定し、この時間を使用してセカンダリバックグラウンドタスクを実行するにはどうすればよいでしょうか? このために、最初にそのようなタスクのコードを関数にラップし、次のようにデバウンサーに渡します。



 var saveAppStateWhenIdle = debounce(1000, saveAppState);
      
      







アプリケーションの重要な場所では、



 saveAppStateWhenIdle ();
      
      







したがって、アプリケーションが「停止」し、重要なリソースが解放されるまでsaveAppStateの実行を延期します。これは非同期コードでも機能します。 私の特定のケースでは、アプリケーションの状態を保存する必要がある他の場所と同様に、すべてのオフキャンバスアニメーションフレームでsaveAppStateWhenIdleを呼び出し、カウントがフレームに移動します。



定期的に繰り返されるコードのデバウンス



別の例として、最も一般的な(マイクロ)ブログがあると想定できます。このブログでは、AJAXによって新しい投稿がロードされ、スクロールと同時に更新間隔が一致する場合にのみ、アプリケーションが。 些細なことですが、不快です。 この問題を解決するには、次のソリューションを作成できます。



 var loadPostsWhenIdle = debounce(1000, loadMorePosts); setInterval(loadPostsWhenIdle, 10000); $(window).scroll(loadPostsWhenIdle);
      
      





したがって、アプリケーションがアイドル状態の場合、新しい投稿は約11秒ごとに読み込まれ、ユーザーがその時点でページをスクロールすると、ユーザーがスクロールを完了するまでダウンロードが遅延します。



残念ながら、このメソッドには1つの欠点があります。つまり、スクロールが完了するたびにloadPostsWhenIdleが呼び出されます。たとえそれらが11秒未満経過した場合でも、つまり、「11秒に1回以下」 この問題を解決するには、ブールスイッチを使用できます。ブールスイッチでは、「true」はアプリケーションがビジーであることを意味し、「false」はアイドル状態であることを意味します。



 var appIsBusy = false; var onAppIsIdle = debounce(1000, function(){ appIsBusy = false; }); var doingPerformanceHeavyStuff = function(){ appIsBusy = true; onAppIsIdle(); } setInterval(function (){ if(!appIsBusy) loadMorePosts(); }, 10000); $window.scroll(doingPerformanceHeavyStuff);
      
      







これで、新しい投稿は10秒に1回しか読み込まれなくなりますが、スクロール中は読み込まれません。



原則として、 doingPerformanceHeavyStuffをアプリケーションの重要な部分に挿入したり、バックグラウンドまたは定期的なタスクでappIsBusyスイッチを使用して、この時間がそのタスクに適しているかどうかを確認したりできます。



All Articles