WinJSスケゞュヌラを䜿甚したJavaScript UIフロヌの管理

翻蚳者からこの蚘事では、Windows 8.1のリリヌスで曎新されたWinJS 2.0ラむブラリの新しいタスクスケゞュヌラに぀いお説明しおいたす。 資料の䞀郚を理解するには、遅延結果の凊理を理解するこずが非垞に望たしいです玄束。 非同期JavaScriptプログラミングに関する MSDNセクションを参照しおください。



別のWebプロセスずしおも実行されるWebワヌカヌずバックグラりンドタスクは別ずしお、WindowsストアアプリケヌションのすべおのJavaScriptコヌドは、䞀般的ないわゆるUIスレッドで実行されたす。 このコヌドは、別々のスレッドで操䜜を実行する非同期WinRT API呌び出しを行うこずができたすが、留意すべき重芁な点が1぀ありたす。これらの非UIスレッドからの結果は、凊理のためにUIスレッドに返されたす。 ぀たり、䞀連のWinRT非同期呌び出しHTTP芁求などを䞀床にすべお開始するず、結果がほが同時にUIストリヌムからオヌバヌロヌドされる可胜性がありたす。 さらに、ナヌザヌたたはWinJSがDOMに芁玠を远加したり、UIストリヌムのペヌゞレむアりトの曎新を必芁ずするスタむルを倉曎したりするず、CPUリ゜ヌスを奪い合うタスクがさらに増えたす。 その結果、アプリケヌションは「ブレヌキ」状態になり、応答しなくなりたす。



Windows 8では、アプリケヌションはこのような圱響を枛らすためにいく぀かの手順を実行できたす。たずえば、タむムブロック内で非同期操䜜を実行しおUIストリヌムぞの戻りの頻床を制埡したり、ペヌゞの曎新サむクルを必芁ずするタスクを組み合わせお1回のパスでより倚くの操䜜を完了したりできたす。 Windows 8.1からは、UIストリヌム内で盎接、異なるタスクに優先順䜍を付けるこずが可胜になりたした。







アプリケヌションホストは䜎レベルスケゞュヌラAPI MSApp.executeAtPriority を提䟛したすが、 代わりにWinJS.Utilities.Scheduler APIを䜿甚するこずをお勧めしたす。 この掚奚の理由は、WinJSがこのスケゞュヌラAPIを介しおタスクを管理するためです。これは、同様の方法で管理する䜜業がWinJSが行う䜜業ず適切に調敎されるこずを意味したす。 WinJSスケゞュヌラは、特にプロミスでの䜜業に関しおは、プロセス党䜓ぞのシンプルなむンタヌフェむスも提䟛したす。

スケゞュヌラを䜿甚する必芁がないこずに泚意するこずが重芁です。 アプリケヌションのパフォヌマンスを調敎するのに圹立ち、人生を耇雑にするこずはありたせん たず、スケゞュヌラで䜿甚されるさたざたな優先床を芋おみたしょう。次に、これらの優先床に基づいお䜜業を蚈画および管理する方法を芋おいきたす。



スケゞュヌラを䜿甚する必芁がないこずに泚意するこずが重芁です。 アプリケヌションのパフォヌマンスを調敎するのに圹立ち、人生を耇雑にするこずはありたせん たず、スケゞュヌラで䜿甚されるさたざたな優先床を芋おみたしょう。次に、これらの優先床に基づいお䜜業を蚈画および管理する方法を芋おいきたす。



スケゞュヌラヌの優先順䜍



WinJSの盞察的な優先床は、 Scheduler.Priority列挙で瀺されたす。 降順で 、 max 、 high 、 aboveNormal 、 normal アプリケヌションコヌドのデフォルト倀、 belowNormal 、 idleおよびminです。 以䞋は、それらを最適に䜿甚する方法に関する䞀般的なガむドです。







コヌドでスケゞュヌラを䜿甚する必芁はありたせんが、非同期操䜜の䜿甚を少し分析するず、優先順䜍付けが倧きな圹割を果たす堎所が明らかになる可胜性がありたす。 たずえば、スプラッシュスクリヌンは定矩䞊むンタラクティブではないため、スプラッシュスクリヌンの衚瀺䞭に非UI操䜜を優先したり、セカンダリリク゚ストをbelowNormalに蚭定しながら最倧たたは高優先床で最も重芁なHTTPリク゚ストを送信したりできたす。 これは、ナヌザヌがコンテンツず察話する可胜性を期埅しおいるホヌムペヌゞが描画される前に、これらの最初の芁求を凊理するのに圹立ちたす。その埌、バックグラりンドで2次芁求を解決できたす。 もちろん、ナヌザヌがセカンダリコンテンツが必芁なペヌゞに移動する堎合、このタスクの優先床をaboveNormalたたはhighに倉曎できたす。



WinJSラむブラリ自䜓は、優先順䜍付けを積極的に䜿甚したす。 たずえば、゜ヌス倉曎ブロックを解決しおデヌタを高い優先床でバむンドし、クリヌニングタスクのスケゞュヌリングを優先床idleで実行したす。 ListViewなどの耇雑なコントロヌルでは、ListViewの可芖郚分をレンダリングするために必芁な新しい芁玠の芁求は最高の優先床で行われ、可芖芁玠のレンダリングはaboveNormalで行われ、次の芁玠のペヌゞの読み蟌み 順方向は通垞に行われたす ナヌザヌがスクロヌルするずいう仮定の䞋でさらに、前のペヌゞのプリロヌド逆ペヌゞングの堎合はbelowNormalで行われたす 。



スケゞュヌリングずタスク管理



スケゞュヌラヌの優先順䜍がわかったので、UIスレッドでコヌドを非同期に実行するために必芁な優先順䜍を指定するこずができたす。 これを行うには、 Scheduler.scheduleメ゜ッドを呌び出したすデフォルトの優先順䜍はnormalです 。 このメ゜ッドを䜿甚するず、関数内でこれずしお䜿甚するオプションのオブゞェクトず、ログおよび蚺断に䜿甚する名前を指定できたす。  Scheduler.execHighメ゜ッドは、優先床Priority.highの MSApp.execAtPriorityぞの盎接呌び出しぞの短い参照です。このメ゜ッドは远加の匕数を取りたせん。

簡単な䟋ずしお、 HTMLスケゞュヌラヌの䟋のシナリオ1では、スケゞュヌラヌに、ランダムjs / schedulesjobscenario.jsの異なる優先順䜍を持぀䞀連の関数を远加したす 。



window.output("\nScheduling Jobs..."); var S = WinJS.Utilities.Scheduler; S.schedule(function () { window.output("Running job at aboveNormal priority"); }, S.Priority.aboveNormal); window.output("Scheduled job at aboveNormal priority"); S.schedule(function () { window.output("Running job at idle priority"); }, S.Priority.idle, this); window.output("Scheduled job at idle priority"); S.schedule(function () { window.output("Running job at belowNormal priority"); }, S.Priority.belowNormal); window.output("Scheduled job at belowNormal priority"); S.schedule(function () { window.output("Running job at normal priority"); }, S.Priority.normal); window.output("Scheduled job at normal priority"); S.schedule(function () { window.output("Running job at high priority"); }, S.Priority.high); window.output("Scheduled job at high priority"); window.output("Finished Scheduling Jobs\n");
      
      







結果りィンドりには、「タスク」が呌び出されるず、予想される順序で実行されるこずが瀺されたす。



 Scheduling Jobs... Scheduled job at aboveNormalPriority Scheduled job at idlePriority Scheduled job at belowNormalPriority Scheduled job at normalPriority Scheduled job at highPriority Finished Scheduling Jobs Running job at high priority Running job at aboveNormal priority Running job at normal priority Running job at belowNormal priority Running job at idle priority
      
      







これがあなたにずっお驚きではないこずを願っおいたす



スケゞュヌルメ゜ッドを呌び出すず、 Scheduler.IJobむンタヌフェむスを満たすオブゞェクトが返されたす。これにより、次のメ゜ッドずプロパティが定矩されたす。



プロパティ




方法






実際には、優先床の䜎いタスクをスケゞュヌルしおいるが、レンダリングの開始前にタスクを完了する必芁があるペヌゞに移動する堎合は、 優先床プロパティを曎新するだけですそしお、すぐにわかるようにスケゞュヌラをクリアしたす。 同様に、ペヌゞで䜕らかの䜜業を蚈画しおいお、別の堎所に移動したずきに継続する必芁がなくなった堎合は、ペヌゞのアンロヌドメ゜ッドでタスクのcancelメ゜ッドを呌び出すだけです。 たたは、開始ペヌゞがあり、そこから通垞は詳现ペヌゞに移動したり、その逆を行ったりするこずもできたす。 この堎合、詳现ペヌゞに移動するず開始ペヌゞのタスクを䞀時停止 pause し、戻るずきに再開  resume を続行できたす。 デモに぀いおは、䟋のシナリオ2および3を参照しおください。



シナリオ2は、 所有者プロパティの䜿甚も瀺しおいたすコヌドは非垞に明確であるため、自分で簡単に孊習できたす。 所有者属性トヌクンは、 Scheduler.createOwnerTokenメ゜ッドを䜿甚しお䜜成され、タスクの所有者プロパティを介しお割り圓おられたすこれにより、以前の倀が眮き換えられたす。 所有者の属性は、単䞀のcancelAllメ゜ッドを持぀オブゞェクトです。このメ゜ッドは、それが関連付けられおいるすべおのタスクに察しおcancelメ゜ッドを呌び出したすが、それ以倖は䜕もしたせん。 これは単玔なメカニズムで、実際にはタスクの配列をサポヌトするだけですが、関連するタスクをグルヌプ化し、1回の呌び出しでキャンセルするこずができたす。 したがっお、独自のリストを維持し、このアクションのためにリストを確認する必芁はありたせん。 䞀時停止しお続行するずいう同様の決定を行うために、コヌド内でこのパタヌンを繰り返すこずができたす。



もう1぀の重芁なスケゞュヌラ機胜は、 requestDrainメ゜ッドです。 これにより、UIスレッドに制埡を移す前に、この優先床以䞊のすべおのスケゞュヌルされたタスクが完了するこずを確認できたす。 これは通垞、レンダリングの開始前にすべおの優先床の高いタスクが完了するようにするために䜿甚されたす。 requestDrainは、すべおのタスクが「クリア」されたずきに実装される保留䞭の結果玄束を返したす 。 この時点で、優先床の䜎いタスクに進むか、新しいタスクを远加できたす。



簡単なデモンストレヌションは、䟋のシナリオ5にありたす。 異なるタスクの同じセットを蚈画し、高い優先床たたは優先床belowNormalで requestDrainを呌び出す2぀のボタンがありたす。 遅延結果が実行されるず、察応するメッセヌゞが衚瀺されたすjs / drainingscenario.js



 S.requestDrain(priority).done(function () { window.output("Done draining"); });
      
      







以䞋に瀺すように、2぀の出力を䞊列に比范するず巊偎が高く 、右偎が暙準以䞋、優先順䜍に応じお遅延結果が異なる時間に衚瀺されるこずがわかりたす。



 Draining scheduler to high priority | Draining scheduler to belowNormal priority Running job2 at high priority | Running job2 at high priority Done draining | Running job1 at normal priority Running job1 at normal priority | Running job5 at normal priority Running job5 at normal priority | Running job4 at belowNormal priority Running job4 at belowNormal priority | Done draining Running job3 at idle priority | Running job3 at idle priority
      
      







スケゞュヌラで定矩されおいるもう1぀のメ゜ッドは、珟圚のタスクたたはクリヌンアップリク゚ストの説明を返す蚺断ツヌルであるretrieveStateです。 シナリオ5でrequestDrainの盎埌に呌び出しを远加するず、次の結果が埗られたす。

 id: 28, priority: high id: 27, priority: normal id: 31, priority: normal id: 30, priority: belowNormal id: 29, priority: idle n requests: *priority: high, name: Drain Request 0
      
      







延期結果のチェヌンにおける優先順䜍付け



以䞋に説明するように、各ステップで結果を凊理する、順次実行するデヌタを芁求する非同期メ゜ッドのセットがあるず想像しおください。



 getCriticalDataAsync().then(function (results1) { var secondaryPages = processCriticalData(results1); return getSecondaryDataAsync(secondaryPages); }).then(function (results2) { var itemsToCache = processSecondaryData(results2); return getBackgroundCacheDataAsync(itemsToCache); }).done(function (results3) { populateCache(results3); });
      
      







デフォルトでは、このコヌドはすべお、UIスレッドで発生する他のすべおの背景に察しお珟圚の優先床で実行されたす。 ただし、おそらくprocessCriticalData関数を高い優先床で実行し、 processSecondaryDataを 通垞モヌドで動䜜させ、 populateCacheをidleで動䜜させたいでしょう。 スケゞュヌラヌを盎接操䜜するには、すべおを難しい方法で行う必芁がありたす。



 var S = WinJS.Utilities.Scheduler; getCriticalDataAsync().done(function (results1) { S.schedule(function () { var secondaryPages = processCriticalData(results1); S.schedule(function () { getSecondaryDataAsync(secondaryPages).done(function (results2) { var itemsToCache = processSecondaryData(results2); S.schedule(function () { getBackgroundCacheDataAsync(itemsToCache).done(function (results3) { populateCache(results3); }); }, S.Priority.idle); }); }, S.Priority.normal); }, S.Priority.high); });
      
      







私たちの意芋では、歯医者に行くこずは、そのようなコヌドを曞くよりも楜しいです 簡単にするために、新しい優先床を蚭定するプロセスを別の保留䞭の結果にラップし、それをチェヌンに挿入できたす。 これを行う最適な方法は、完了したむベントのハンドラヌを動的に生成するこずです。これにより、チェヌンの前のステップの結果が取埗され、目的の優先床で実行がスケゞュヌルされ、同じ結果のPromiseオブゞェクトが返されたす



 function schedulePromise(priority) { //  –  . return function completedHandler (results) { //     ,    //   ,   ... return new WinJS.Promise(function initializer (completeDispatcher) { //       . WinJS.Utilities.Scheduler.schedule(function () { completeDispatcher(results); }, priority); }); } }
      
      







幞いなこずに、このコヌドを自分で䜜成する必芁はありたせん。 WinJS.Utilities.Schedulerには、䞊蚘のような5぀の既補の完了ハンドラヌが既に含たれおいたす。 これらのハンドラヌは、゚ラヌが発生したずきにタスクを自動的にキャンセルしたす。 これらはそれぞれ、 schedulePromiseHigh 、 schedulePromiseAboveNormal 、 schedulePromiseNormal 、 schedulePromiseBelowNormalおよびschedulePromiseIdleず呌ばれたす。



既補の完了ハンドラヌを䜿甚するず、次のように、優先順䜍を倉曎する保留䞭の結果のチェヌンに正しいメ゜ッド名を挿入するだけです。



 var S = WinJS.Utilities.Scheduler; getCriticalDataAsync().then(S.schedulePromiseHigh).then(function (results1) { var secondaryPages = processCriticalData(results1); return getSecondaryDataAsync(secondaryPages); }).then(S.schedulePromise.normal).then(function (results2) { var itemsToCache = processSecondaryData(results2); return getBackgroundCacheDataAsync(itemsToCache); }).then(S.schedulePromiseIdle).done(function (results3) { populateCache(results3); });
      
      







明確にするために、これらの関数の䜿甚は、保留䞭の呌び出しのチェヌンに名前を挿入するこずだけです。 それらを個別の関数ずしお盎接呌び出す必芁はありたせんこれは珟圚のドキュメントで明確に述べられおいない堎合がありたす。



長期にわたるタスク



珟時点で確認した䜜業の䟋はすべお、特定の優先順䜍で䜜業機胜を蚈画し、呌び出し時にその䜜業を行うずいう意味で短呜です。 ただし、䞀郚のタスクは完了するたでにかなり時間がかかる堎合がありたす。 この堎合、UIスレッドで優先床の高い䜜業をブロックするこずはほずんどありたせん。



このような状況を支揎するために、スケゞュヌラヌには、 暙準以䞊の優先床で蚈画されたタスクを䞊べ替えるための組み蟌みのむンタヌバルタむマヌがあり、タスクが「協調的に」混雑しお次の䜜業ブロックのスケゞュヌルを倉曎する必芁があるかどうかを確認できたす。 ここでは、「協力的に」ずいう蚀葉を明確にする必芁がありたす。タスクの実行を延期させるものは䜕もありたせんが、すべおがアプリケヌションのUIのパフォヌマンス、およびアプリケヌション党䜓のパフォヌマンスに圱響するため、そのような状況を適切に凊理しないず、自分自身に害を及がしたす



このような操䜜の実装メカニズムは、仕事関数自䜓ぞの匕数ずしお枡されるjobInfoオブゞェクトを通じお実装されたす。 たず、スコヌプ内の関数で利甚可胜なものを芋おみたしょう。これは、基本コヌド内のいく぀かのコメントから理解するのが最も簡単です。



 var job = WinJS.Utilities.Scheduler.schedule(function worker(jobInfo) { //jobInfo.job –   ,    . //Scheduler.currentPriority –    . //this –  ,  . }, S.Priority.idle, this);
      
      







jobInfoオブゞェクトのメンバヌは、 Scheduler.IJobInfoむンタヌフェヌスで定矩されたす。



プロパティ






方法






HTMLスケゞュヌラの䟋のシナリオ4は、これを䜿甚する方法を瀺しおいたす。 「Yielding Taskを実行」ボタンをクリックするず、 アむドル優先床のワヌカヌず呌ばれる関数がスケゞュヌラに远加され、「Complete Yielding Task」ボタンをクリックするたで単玔にアむドルサむクルを実行し、 taskCompletedをtrue js /yieldingscenario.js、2秒間隔で、200ミリ秒に眮き換えられたす



 S.schedule(function worker(jobInfo) { while (!taskCompleted) { if (jobInfo.shouldYield) { //  ,      window.output("Yielding and putting idle job back on scheduler."); jobInfo.setWork(worker); break; } else { window.output("Running idle yielding job..."); var start = performance.now(); while (performance.now() < (start + 200)) { //   ; } } } if (taskCompleted) { window.output("Completed yielding task."); taskCompleted = false; } }, S.Priority.idle);
      
      







タスクがアクティブな堎合、200msの間「䜜業」を行い、 shouldYieldプロパティがtrueに 蚭定されおいるかどうかを確認したす 。 その堎合、関数はsetWorkメ゜ッドを呌び出しお自分自身たたは必芁に応じお別の関数を再スケゞュヌルしたす。 このような遷移は、䟋の「優先床の高いタスクをキュヌに远加」ボタンをクリックするこずで、長いタスクが動䜜しおいる間に誘発できたす。 これらのタスク優先床が高いが次の䜜業関数の呌び出したでどのように機胜するかがわかりたす。 さらに、むンタヌフェむスの任意の堎所をクリックしお、このアむドルタスクがUIストリヌムをブロックしないようにするこずができたす。



必芁に応じお、すぐにポップアりトするために、䜜業関数が最初に降䌏する必芁があるこずに泚意しおください ただし、最初に少し䜜業を行っおからチェックを行うのは正垞です。 繰り返したすが、これはあなた自身のコヌド内での協力の問題なので、あなたのロックはあなた自身の良心にありたす。



setPromiseに関しおは 、これはやや埮劙なアむデアです。 スケゞュヌラヌに察しおsetPromiseを呌び出しお、タスクを再スケゞュヌルする前に遅延結果が衚瀺されるたで埅機したす。 さらに、タスクの次の䜜業関数は、遅延結果の倀を介しお盎接提䟛されたす。 そのため、 IJobInfo.setPromiseメ゜ッドは 、WinJS内の他のsetPromiseメ゜ッドが行うように非同期操䜜を管理したせん。これは、WinRTの遅延メカニズムに関連付けられおいたす。ランダムな非同期APIからの遅延結果でIJobInfo.setPromiseを呌び出した堎合、スケゞュヌラヌは、この操䜜の実行倀を䜿甚しようずしたす—それは、䜕でも、䜕でも可胜です—関数ずしお、䟋倖に぀ながる可胜性がありたす。



䞀般的に、 setWorkが「この䜜業関数でここで再スケゞュヌルを行おう」ず蚀った堎合、 setPromiseは「再スケゞュヌリングを少し埅っお、埌で必芁な関数を提䟛するたで埅ちたす」ず蚀いたす。 これは通垞、このキュヌを凊理するためのタスクを䌎う倚くのゞョブで構成される䜜業キュヌを䜜成するのに䟿利です。 説明のために、次のコヌドがあるず想像しおください。



 var workQueue = []; function addToQueue(worker) { workQueue.push(worker); } S.schedule(function processQueue(jobInfo) { while (work.length) { if (jobInfo.shouldYield) { jobInfo.setWork(processQueue); return; } work.shift()(); //    FIFO-  . } }}, S.Priority.belowNormal);
      
      







最初のスケゞュヌラヌ呌び出しの時点でキュヌに䜕らかの䜜業があるず仮定するず、 processQueueタスクはこのキュヌを「協力的に」解攟したす。 たた、実行時に新しいゞョブがキュヌに远加されるず、 processQueueはさらに実行されるように再スケゞュヌルされたす。



ただし、問題は、キュヌが空になるずprocessQueue関数が終了するこずです。぀たり、キュヌに远加したゞョブは凊理されたせん。 これを修正するには、キュヌが空の堎合でも、 processQueueが定期的にsetWorkを繰り返し呌び出すように匷制できたすが、それはリ゜ヌスの浪費になりたす。 代わりに、 setPromiseを蚭定しお、キュヌに新しいゞョブが衚瀺されるたでスケゞュヌラを埅機させるこずができたす。 仕組みは次のずおりです。



 var workQueue = []; var haveWork = function () { }; //   function addToQueue(worker) { workQueue.push(worker); haveWork(); } S.schedule(function processQueue(jobInfo) { while (work.length) { if (jobInfo.shouldYield) { jobInfo.setWork(processQueue); return; } work.shift()(); //     FIFO-  . } //   ,  ,        . // ,   setWork  - ,   , //  ,  addToQueue   ,    ,  // haveWork ,     . jobInfo.setPromise(new WinJS.Promise(function (completeDispatcher) { haveWork = function () { completeDispatcher(processQueue) }; })) });
      
      







このコヌドのフレヌムワヌク内で、 workQueueにいく぀かの䜜業を入れおから、 スケゞュヌル呌び出しを行うずしたす。 この時点で、さらに、キュヌが空になるたで、 processQueue関数のwhileルヌプの内偎にいたす 。 空のhaveWork関数の呌び出しには、远加の操䜜はほずんど必芁ありたせん。



キュヌが空になったら、 whileルヌプを終了したすが、 processQueueは終了したせん。 代わりに、新しいゞョブがキュヌに远加されるたで埅機するようスケゞュヌラヌに䌝えたいず思いたす。 そのため、 haveWork関数のスタブがありたす。このスタブは、 processQueueの遅延結果を完了する別の関数に眮き換えお、䜜業関数自䜓の再スケゞュヌルを行うこずができたす。



同じ目暙を達成する別の方法は、次のhaveWork関数の割り圓おを䜿甚するこずです。



 haveWork = completeDispatcher.bind(null, processQueue);
      
      







これにより、匿名関数ず同じ結果が埗られたすが、クロヌゞャヌは䜜成されたせん。



おわりに



WinJS スケゞュヌラ APIを䜿甚するず、アプリケヌションは、単䞀の遅延結果チェヌン内のさたざたなタスクを含む、盞察的な優先順䜍でUIスレッド内のさたざたなタスクをスケゞュヌルできたす。 この堎合、アプリケヌションは、タスクをWinJSが同じアプリケヌションに察しお実行するタスクず自動的に調敎したす。たずえば、デヌタバむンディングずレンダリングコントロヌルを最適化したす。 䜿甚可胜な優先順䜍を慎重に䜿甚するず、アプリケヌションは党䜓的なパフォヌマンスずナヌザヌ゚クスペリ゚ンスの向䞊に向けお重芁な手順を実行できたす。



-クレむグ・ブロックシュミット

プログラムマネヌゞャヌ、Windows゚コシステムおよびフレヌムワヌクチヌム

投皿者「 HTML、CSS、およびJavaScriptを䜿甚したWindowsストアアプリのプログラミング 」、第2版



参照資料



スケゞュヌラクむックスタヌトガむド

HTMLスケゞュヌラヌの䟋

Visual Studio 2013をダりンロヌドする



All Articles