紹介です。 現代のウェブアプリは重く感じることもあります。時には、長時間実行中のJavaScript機能だけでインターフェイスを凍結するのに十分で、ユーザーは不満を抱え、アプリがまだ動いているか凍結しているか不確実です。 小さいけど強力な方法を紹介します。 ユーザーが実行を停止させ、ブラウザにより重要なタスク(クリックや入力など)を処理する機会を与え、その後、停止した場所に直ちに続けることができます。 , explore old workarounds, and see how 人生を楽にする 優先化タスクスケジュール API トップ > トップ > トップ > トップ > Main Thread scheduler.yield() どういうことですか?Yield(?) だから、何が それは、The Method of the インタフェース from the new この方法により、開発者としてJavaScriptの実行を停止し、明示的にコントロールを元に戻すことができます。 – ユーザーのインタラクション、クリック、入力など、待機中の他の重要なタスクを処理し、終了した場所から実行を続けることができます。 あなたはブラウザに言う: scheduler.yield() 日程 優先順位のタスクスケジュール API Main Thread scheduler.yield() 「待って、息を吸い、現在の任務を休止し、他の重要な任務に焦点を当てましょう。 "Wait, take a breath, let's pause the current task and focus on other no less or more important tasks. Once you've done, come back and continue execution from where we left off." これは、特に長いまたは重いJavaScriptタスクを実行するときに、あなたのページをより応答性の高いものにします。 つまり、ブラウザがユーザーの入力にどれだけ迅速に反応するかということです。 Next Paint(INP)のインタラクション ターミナル より深く掘り下げる前に、記事全体で使用されるいくつかの基本的な用語を迅速に行きましょう。 Main Thread - これは、ブラウザがその仕事のほとんどをやっている中央の場所です. It handles rendering, layout, and runs most of your JavaScript code. 長いタスク - これは、通常50ミリ秒を超える長い間、メイン・トレードを忙しく保つJavaScriptのタスクです。 ブロックタスク - ブラウザがクリックに応答したりUIを更新したりするなど、他の重要なことを処理するのを防ぐメイン・トレード上の同期操作です。 問題の 美しさを理解するために、 まず、問題を解決しようとしていることを理解する必要があります。JavaScriptは単一のトレードで動作します. それは一度に1つのことしかできません. あなたのコードがトレードを忙しくしているなら、他のすべてのもの - レンダリング、ボタンクリック、入力の入力は待たなければなりません. 理想的な世界では、あなたは常に重いタスクを小さな部分に分割します. しかし、現実は混乱します. あなたは遺伝コード、サードパーティのスクリプト、または避けられない重い計算に取り組んでいます. そしてそれが起こると、ユーザーは凍結されたページに閉じ込められます。 scheduler.yield() JavaScript 実行モデル 迅速なリフレッシュとして、JavaScriptがタスクをどのように処理するかを示す示例図があります。 私は、あなた方の多くがこれまで、タスク列、イベントループ、呼び出しステックなどのこのような図図を見たことは確かです。 JavaScript Execution Model 主なアイデアを一歩ずつ踏み出そう: All synchronous code goes straight to the Call Stack and runs line by line, function by function. It follows the LIFO principle – last in, first out. JavaScript runs in a single thread, which means it can only do one thing at a time. すべての同期コードは、Call Stackに直接行き、行列ごとに、機能ごとに実行されます。 Asynchronous operations (like setTimeout, fetch) are handled outside of the Main Thread – by the Web APIs (provided by the browser or environment). Once they are done, they don’t go back directly into the Call Stack. Instead, their callbacks are queued – either in the microtasks queue (e.g. Promise.then, queueMicrotask) or the macrotasks queue (e.g. setTimeout, setInterval). 非同期的な操作(例えばsetTimeout、fetch)はメイントレードの外部で Web API によって処理されます。 Call Stack が空の場合、Event Loop はマイクロタスクの列をチェックし、すべてのマイクロタスクを順番に1つずつ実行します。 その後、列から1つのマクロタスクを取り出して実行します。 プロセス中に新しいマイクロタスクが追加される場合は、次のマクロタスクの前に実行されます。 このループは続きます:すべてのマイクロタスク → 1 つのマクロタスク → 繰り返します。 新しい同期コードは、ユーザーがボタンをクリックし、新しいスクリプトが実行されるか、マイクロタスクまたはマクロタスクがそのコールバックを実行するなど、新しいタスクが到着したときに、Call Stack に入ります。 これは非常に簡潔で表面的な説明であり、それがどのように機能するかをあなたに思い出させるためです。 問題の説明 あなたがJavaScriptがタスクを実行する方法についての理解をリフレッシュした今では、このモデルに伴う実際の問題をより詳しく見てみましょう。問題は単純です:タスクがメイントレードであまりにも長くかかると、それは他のすべてをブロックします - ユーザーの相互作用、更新、アニメーション。これはUIを凍結させ、メイントレードをブロックするのを避けることを意味します。明らかな最初の考えは、「いいえ、長い機能や重い機能を書かないで、それはそれです。問題は解決されています!」そして、そう、それは真実です - 理想の世界では、あなたは常に重いコードをより小さな部分に分割し、すべてを最適化し、メイントレードをブロックするのを避けるでしょう。しかし、正直 そのために、いわゆる機能を作成します。 この機能は、指定された期間の間、メインスレッドのブロックタスクとして機能します. The function simulates this type of "heavy" computation on each element of the array. blockingTask() 残念ながら、コードブロックは行番号を表示しません。私の説明では、たまに特定の行(例えば、「行5はXです」)を参照します。 残念ながら、コードブロックは行番号を表示しません。私の説明では、たまに特定の行(例えば、「行5はXです」)を参照します。 function blockingTask(ms = 10) { const arr = []; const start = performance.now(); while (performance.now() - start < ms) { // Perform pointless computation to block the CPU. arr.unshift(Math.sqrt(Math.random())); } return arr; } 機能に関して幻想的なものは何もありません、これがすべてです: これは、数ミリ秒であるという論点を受け入れます. This is the minimum time the function will run, thus occupying the main thread. これは、関数が実行される最小時間です。 空のマレージを作成します。 開始時間(現在の時間として)を作成します。 その後、指定された時間が過ぎるまで、しばらくロープを実行します。 ループの内部では、ランダムで無意味な計算を行い、負荷をシミュレートします。 最後に、計算の結果を返します。 この機能は役に立たないが、重荷の現実世界のシナリオをシミュレートする。 データの範囲をスループし、その重い仕事を各項目に適用する必要がある共通の状況を想像してください。 これを実現するために、我々はA 機能: heavyWork() function heavyWork () { const data = Array.from({ length: 200 }, (_, i) => i) const result = [] for (let i = 0; i < data.length; i++) { result.push(blockingTask(10)) } return result; } 以下が起こる場合: ライン2では、0から199までの数だけの200項目の数を生成します。200項目はそれほど多くはありませんが、問題の本質を見るのに十分です。 その後、処理された値を格納するための新しい空の「結果」マレーが作成されます。 ライン 5 は、データ マレージの全長を通過するループを宣言します。 ループの中で、私たちはblockingTask() 関数を実行し、各要素の作業の 10 ミリ秒をシミュレートし、結果は「結果」のマレーに追加されます。 もう一度、私は、デモのために、blockingTask() 関数はセマンティックな負荷を負いませんことを覚えておきたい。 それは単にいくつかの想像上の、リソース密集した作業を実行します。 現実世界では、それはマレー要素のいくつかの労働密集した処理かもしれません。 最後に、生成されたマレーを返します。 要素ごとにわずか10ミリ秒、およびわずか200の要素 - しかし、一緒に、彼らは 2 完全な秒間、メイン トレードをブロックします。 問題のデモです。 これはまだ完全なデモではありません - あなたが問題を明確に見るのに役立つシンプルな視覚として考えてください。 こちらはあなたが見るもの: 「Configuration」と題された左のウィンドウでは、ブロックTask() 関数が実際に実行されているかどうかを意味するメイン トレードのブロックをオンとオフにします。 「Heavy Task」と題されたウィンドウは、heavyWork() 関数を実行します. This is the one that processes an array using blockingTask() on each element if the main thread blocking is enabled. そして、「Logger」と題されたウィンドウは、ミリ秒を含むコンソールに現在の時間をログします。 見てみよう、何が起きるときに、 ブロックはオフなので、タスクは非常に簡単です. それは、複雑な計算なしで、200の要素のマレージ上のループです。 Main Thread あなたが見ているもの: ユーザーは「OK」ボタンをクリックし、heavyWork() 関数が実行され、すぐに返されます。これは、コンソール内の HEAVY_TASK_DONE メッセージに示され、その後、結果 - 数値の数列が表示されます。 その後、ユーザーは「ログ」ボタンを3回クリックして、コンソールに現在の時間をログします - タイムスタンプがすぐに表示されます。 ユーザは heavyWork() 関数を再度実行し、再び即時応答します。 最後に、ユーザーは2つのウィンドウを閉じ、実際にはDOMからそれらの要素を削除します。 この場合、すべてが迅速かつ応答性を感じます. ブラウザは主なトレードが無料であるため、相互作用を処理するのに問題はありません. タスクはほぼ瞬時に一貫して実行されます. さて、今度は、The ブロックするので、マレージの各要素に対しては、 機能はわずか10ミリ秒の遅延で呼ばれる。 Main Thread blockingTask() そして今では、UI要素とのユーザーの相互作用がより滑らかになり、UIの凍結が現れたことを観察することができます。 ユーザーは「OK」ボタンを押して、 heavyWork() 関数を起動します。そして起こる最初の遅れは、「OK」ボタンが視覚的に押され続けることです。 なぜですか? ブラウザが heavyWork() をブロックしている間、再塗装できないからです。 この期間中、ユーザーは「ログ」ボタンを4回クリックします - 何も起こりません。クリックは登録され、そのマネージャーは列に追加されますが、ブラウザは反応できません。 heavyWork() が終了した後のみ、コンソール出力が表示されます:最初に heavyWork() の結果、その後4つのタイムスタンプ - すべてがバッチで印刷されます。 次に、ユーザーは「OK」ボタンを再度クリックします。同じ行動 - タップを押します。それから、heavyWork() タスクが実行されている間、彼は「X」のアイコンを3回クリックしてウィンドウを閉じようとします。再び、視覚的な応答はありません。タスクが終わるときにのみ、ウィンドウが消えることがわかります。 最後に、 heavyWork() を実行し、最後のウィンドウを閉じます。 このシンプルなデモでは、どのくらいの時間をタスクがユーザーのアクションに対応するブラウザの能力をブロックするかを示しています。 各ブロック通話はわずか10ミリ秒かかりますが、それらのうち200を連鎖すると、2秒間の凍結が起こります。 ユーザーはボタンと相互作用することができず、インターフェイスは再塗装されません。 イベントは列に並びますが、通話スタックが明確になるまで処理されません。 これはパフォーマンスの問題ではなく、ユーザー体験の問題です。 それはまさに私たちが解決したい問題の種類です - 理想的には、手動で私たちの論理を数十回のコールバックに分割する必要はありません。 問題の解決 問題を理解した今では、可能な解決策について話しましょう。もちろん、最善の戦略は、最初にコードを効率的に維持し、早期に物事を解くことによって長いタスクを回避することです。しかし、あなたが見たように、物事が起こります。 さまざまなアウトカウントやトリックが登場し、反応能力を向上させるようになったが、それらすべての背後にあるコアアイデアは――そしてその背後にある。 しかも・・・かなりシンプルです: Prioritized Task Scheduling API scheduler.yield() タスクを小さなパーツ、またはいわゆるブロックに分割します。 そして、時々、ブラウザが息を引き取るように休憩します。 言い換えれば、ユーザーのインタラクションやアップデートのレンダリングなど、より緊急なタスクを実行するチャンスをメイン・トレードに与え、自分の仕事を終わらせるために戻ります。 Here is what the concept of 機能は Pseudocode で表示されます: heavyWork() function heavyWork() { // Do heavy work... /** * Take a breather! * Yield the execution to the Main Thread... * */ // Continue to do heavy work... } ここで何が起きているのか: あなたはあなたの任務の一部を実行します。 その後、ブラウザが他の優先順位の高いタスク(UI更新など)を処理できるように停止します。 停止した場所から機能を実行し続ける。 古い問題解決方法 前 長いブロックタスクに対処するための最も一般的なトリックは、使用することでした。 0(ゼロ)の遅延で呼び出すと、そのコールバックタスクを終了時点に追加します。 次に、他のタスクを先に実行することを許可します. 言い換えれば、ブラウザに次のように言います。 scheduler.yield() setTimeout() macrotasks 「このコードを後で実行し、他のすべてを処理した後」 「このコードを後で実行し、他のすべてを処理した後」 それは、あなたが重い仕事の塊の間の主要な糸を短い呼吸を与えることができる方法です。 機能は、このアプローチを使用するように見えるかもしれません: heavyWork() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise(resolve => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } ここで起きていることを分解しましょう: Line 3: A Promise is created and its executor runs immediately, scheduling a setTimeout() with zero delay. The timeout’s callback (which resolves the Promise) is added to the end of the macrotask queue. Because of waiting, the rest of the async function is paused. Technically, this continuation is added to the microtask queue, waiting for the Promise to resolve. The JavaScript engine checks the Call Stack – once it’s empty, the Event Loop kicks in. First, it looks at the microtask queue – but since the Promise is not resolved yet, there’s nothing to run. Then, the Event Loop picks the macrotask from the queue (in our example, it’s the Time setout() callback), runs it, and this resolves the Promise Line 9: We calculate how often we want to yield to the Main Thread, roughly every 25% of the work. この数字は、タスクがどれほど重いかによって異なります。 Lines 13-15: Inside the loop, if the condition for yielding interval is met, execution is transferred to the main thread, that is, the setTimeout() technique is repeated, allowing the browser process user interactions or redrawing the interface. ループ内で、 yielding intervalの条件が満たされた場合、実行はメイン・トレードに転送されます。 基本的に、このアプローチは機能します - 比較的シンプルで、反応能力を向上させます。しかし、妥協があります。 正確なスケジュールのために構築されていません. It places tasks at the end of the macrotask queue, and anything already in that queue can delay your continuation. それは、マクロタスクの列の終わりにタスクを配置し、すでにその列に存在するものは、あなたの継続を遅らせることができます。 setTimeout() たとえば、ページの他の部分が使用しているとします。 定期的に課題を実行する: setInterval() setInterval(() => { /* Another heavy work... */ }) async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise((resolve, reject) => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Now your own task - the next chunk of 機能 - これらの間隔コールバックの1つまたは複数によって遅れる可能性があります. ブラウザは次の行で何でも実行し、あなたは順序を制御しません. 何らかの方法で、あなたは譲歩することができます、あなたは正確にいつあなたがコントロールを取り戻すかを知りません。 heavyWork() setTimeout() 解決方法は他にもあるかもしれないが、それは 次回の再塗装の直前に作業をスケジュールできる機能です。 同様の欠点があるか、または、 ブラウザの空き時間中にコードを実行します。それはまったく選択肢ではありませんが、バックグラウンド、重要な作業のための良いもので、主なトレードがより重要なタスクのために無料になるのを助けます。一般的には、そのような問題を解決し、予防するための他の戦略について議論することができます。 テーブルに持ってくる。 requestAnimationFrame() setTimeout() requestIdleCallback() scheduler.yield() スケジュール(?) – 実行を休止し、メインストリームに制御を生成する現代的な方法で、ブラウザが待機中の高優先度の作業を実行することを可能にし、その後、停止した場所から実行を継続します。 expression が達成され、呼び出された現在の関数の実行が中止され、主なスレッドに制御を生み出し、従って現在のタスクを破壊するか、または停止する。 scheduler.yield() await scheduler.yield() 美しさ of その後の継続は、 列の前方に残っており、走る予定です。 その他の非重要な課題が列に並び、主な違いは、 このアプローチは、With 通常、これらの継続は、すでに列に並んでいた新しいタスクの後に実行され、メイン・トレードへの出力とそれらの完了の間の長い遅れを引き起こす可能性があります。 scheduler.yield() scheduler.yield() BEFORE setTimeout() setTimeout() 以下の図は、実際の3つのアプローチがどのように比較されるかを示しています。 In the first example, without yielding to the main thread: At first, the long " " runs uninterrupted, blocking the main thread and UI accordingly. Then, a user event is processed – a button click triggered during the execution of " ". And finally, " " is executed – callback scheduled earlier or during the execution of the long task. Task 1 Task 1 Task 2 setTimeout() In the second example, using as a yielding to the main thread: The execution queue is different. At first, the long " " runs. Then, when the yield to the main thread happens, " " pauses to let the browser breathe, and the button click is processed. But after the button click is processed, the callback will be executed first, which could have been scheduled in advance or during the execution of " ". And finally, only after that, the continuation of " " will be executed. setTimeout() Task 1 Task 1 setTimeout() Task 1 Task 1 In the last example, using : After the long " " has been paused and the user click event has been processed, then the continuation of " " is prioritized and runs before any queued tasks. scheduler.yield() Task 1 Task 1 setTimeout() In summary, is a more intelligent and predictable way to give the main thread breathing room. It avoids the risk of your code being pushed too far back in the queue and helps maintain performance and responsiveness, especially in complex applications. scheduler.yield() 優先順位 では、何がこのような行動の違いを引き起こすのでしょうか?それは優先順位の問題です! 開発者として、私たちは通常、優先順位の観点でイベントループ内のタスクの実行順序について考えていません。 そして しかし、より深く見ると、プレー中に暗示的な優先順位も存在することに気づくでしょう. たとえば、ユーザーのアクションによって発射されたボタンクリックマネージャーは、通常、プレーの前に実行します。 callback たとえ両方とも 前述したように、 それは、その一部である。 – 幅広く機能豊富なインターフェイスで、独自の完全な議論に値し、この会話の範囲を明らかに超えており、それにもかかわらず、その主要な特徴の1つを挙げることは重要です:明確なタスク優先モデルの導入。 microtasks macrotasks setTimeout() macrotasks scheduler.yield() Prioritized Task Scheduling API 「ユーザーブロック」 - クリック、タップ、重要なUI操作などのユーザーのインタラクションに直接影響を与える最も優先されたタスク。 "user-visible" - UIの可視性やコンテンツに影響を与えるタスクですが、即時入力には重要ではありません。 「バックグラウンド」 - 緊急でないタスクであり、現在のユーザー体験に影響を与えずに安全に延期することができ、ユーザーに目に見えないタスク。 デフォルトで、 「A」 優先順位も、 expose the 上記の優先順位を指定したタスクをスケジュールするために指定されたメソッドですが、ここではこのメソッドについての詳細には入りませんが、 A 内部から予定されていた 優先順位を相続する。 scheduler.yield() user-visible Prioritized Task Scheduling API postTask() scheduler.yield() postTask() どのようにして、planner.yield()を使用するか。 一度、すべてがどのように機能するかを理解すると - タスクの種類、長いブロック操作によって引き起こされる問題、優先順位、 はっきりしているが、賢く慎重に使用すべきである。ここに最新のバージョンがある。 機能 使用 現在、代わりに You just need to call 残りの部分は変わらず。 scheduler.yield() heavyWork() scheduler.yield() setTimeout() await scheduler.yield() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await scheduler.yield() const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await scheduler.yield() } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } ユーザーがAを開始したとき 機能 使用 まず、その違いが目立つ」と、 " button does not stick, and secondly, user clicks events on the " ボタンが付かないと、ユーザーはイベントをクリックします。 「ボタン」がうまく処理され、ユーザーのページとの相互作用をブロックしません。 heavyWork() scheduler.yield() OK Log つまり、最初に、The 機能が起動し、ボタンが粘着せずに再生されました。この重いタスクが実行されていた間に、ユーザーは「 」を押しました。 イベントは成功して処理され、データはコンソールに印刷されました。 機能は続いており、最終結果はコンソールに印刷された。完了後、ユーザーは「 簡単に言うと、あなたはあなたのブラウザに一行だけで休憩を与えることができます。 heavyWork() Log heavyWork() Log デモ 機能の説明 あなたが理論を探索した今、実践し、実際の動作のデモを見ていきましょう. これはシミュレーションされた銀行アプリケーションです. もちろん、それはフィクションと単純化されているが、それはメインストリームのブロックがインタラクティビティにどのように影響を与えるかを理解するのに十分な現実世界の複雑さをキャプチャします。 助けられる。 scheduler.yield() 以下は、ユーザーがインターフェイスで見るもの。 – By default, the account balance is hidden behind a placeholder of asterisks. This is a familiar pattern in real banking apps, where sensitive information is hidden unless explicitly revealed by the user. A button labeled " " toggles visibility. Balance section Show balance – A visual representation of a bank card, shown front side by default, where some details are displayed: card type in the top left corner, last 4 digits of the card, the cardholder's name, and payment system, at the bottom right corner of the card. There are two buttons to the right of the card: Bank card – which flips the card when clicked. The back side of the card reveals sensitive card data like its full number, expiration date, and CVV code. Although the card number is generally not considered private information, some applications still prefer not to show the full number by default, but only if the user initiates it. However, I know and even use banks that generally do not allow you to see the bank card number in the application. Show card details – by clicking this button, this feature supposedly generates a list of transactions on the card and displays them in the table below. It imitates the real functionality where users can generate reports on bank card transactions. In reality, these reports can be complex tables with many customizable filters and the ability to download the report as a file. Such operations might involve heavy computations, process a huge amount of data, making them resource-intensive and time-consuming. For the sake of the demo, it's simplified. Under the hood, the " " button triggers the previously discussed function, which simply blocks the main thread using the function, which was also discussed above. After that, static mock transaction data is simply rendered into the table. Generate report Generate report heavyWork() blockingTask() アプリケーションの動作は、左側の構成パネルのさまざまな設定を使用してカスタマイズできます. You may have noticed its simplified version in earlier screenshots. 今、それが何をしているかを説明する時間です: Main Thread blocking - Main Thread がブロックされるかどうかを決定します. In fact, when this option is enabled, the blockingTask() function is executed. Scheduler.yield() - scheduler.yield() が使用されているかどうかを調べる。 Data array length - ヘビーWork() 関数によって何個の要素が再生されるかを制御します。 ブロック時間 - 処理にかかるマレージの各要素のミリ秒数を指定します。 収益間隔 – scheduler.yield() がどのくらいの頻度で呼ばれるかを割り当て、数値を通じて進歩の割合として定義します. つまり、この数値が低いほど、頻繁に呼ばれるほどです。 以前の例では、10msの遅延と25%の間隔を持つ200要素の数値を使用しました - 過剰な遅延なしに目に見える影響のための良いバランス。 より大きなデータセットでは、より小さな間隔はしばしば良いです。 デモです。 すべての機能性と構成を整理した後、実際の使用シナリオを歩いて、メイン・トレードのブロックがどのようにユーザー体験に影響するかを見ていきましょう。 ブロック & Disable . 私たちはまた、数列の長さを少し増やすので、重い操作はより長くかかりますので、効果を観察する時間を与えます。 「Back the scenes, this triggers the 1000の元素を処理する機能で、各元素は10ミリ秒かかります。 Main Thread scheduler.yield() Generate report heavyWork() 何が起きているかを見てください: The " " button stays stuck, it doesn't unpress, and the UI doesn't re-render. While the report is being generated, the user tries to click the " 「あの時」 インターフェイスは完全に凍結され、アニメーションはなく、フィードバックもなく、進歩感もありません。これは悪いユーザー体験の典型的な例です。アプリは凍結しているように見えますが、技術的にはまだ動作しています。 Generate report Show card details Show balance これらの欠点を解決するために、使用する いくつかの構成を調整することによって。これが現在の構成の見た目です: The 未だにブロックされています. This time, the option to use 配列の長さは、明確性のためにわずかに増加します。ブロック時間は、10ミリ秒と同じです。 応答間隔は、マレーの長さが拡大されたため、よりスムーズな応答性のために5%に短縮されます。 scheduler.yield() Main Thread scheduler.yield() scheduler.yield() そして今、更新された構成で、同じユーザーフローはまったく異なるように見えます。 「ボタンがクリックされ、正しく再生され、ロードアニメーションが表示されます。レポートが生成されている間、ユーザーはユーザーインターフェイスと成功して相互作用します:彼らはカードを振り替え、バランスを切り替えることができます。アプリケーションは、アニメーションがわずかに滑らかであるにもかかわらず、応答し続けます、それは以前の凍結に比べて大きな一歩です。これはより良い経験です。ユーザーは知られ、制御され、アプリケーションが動いているかどうかを推測しません。 もちろん、実際の実装はさらに最適化することができますが、このシンプルな形でさえ、違いは顕著です。 Generate report scheduler.yield() 結論 だから、今日あなたはあなたのブラウザに休憩を与えることについて学びました、その重要性は、 より高い優先順位のタスクを実行し、これらの技術の利点と欠点があります。 この記事で扱われていない他の機能がありますが、私の目標は、あなたに実験を始めるのに十分な堅固な基盤を提供し、あなたのコードがブラウザでどのように再生するかについて異なり考え始めるのに十分でした。 Main Thread Prioritized Task Scheduling API 役に立つ左翼 オンラインデモ – https://let-your-browser-take-a-breather.onrender.com/ デモ GitHub リポジトリ – https://github.com/WOLFRIEND/let_your_browser_take_a_breather グラフ – https://drive.google.com/file/d/1FLKKPaseyypE3pVXXn7Cj0aWac3rCayn/view