入力遅延を最適化する

入力遅延について学び、インタラクティビティを高速化するために遅延を短縮する手法を学びます。

ウェブ上のインタラクションは複雑なもので、ブラウザ内でさまざまなアクティビティが発生してインタラクションを促します。ただし、イベント コールバックが実行されるまでに入力遅延が発生するという点は、どの方法でも共通しています。このガイドでは、入力遅延と、ウェブサイトの操作を高速化するために入力遅延を最小限に抑える方法について説明します。

入力遅延とは何ですか?

入力遅延とは、ユーザーがページを最初に操作したとき(画面のタップ、マウスのクリック、キーの押下など)から、その操作のイベント コールバックが実行を開始するまでの時間です。すべてのインタラクションの開始時には、一定の入力遅延が発生します。

入力遅延の簡素化された可視化。左側には、インタラクションの開始を示すスターバーストが付いたマウスカーソルの線画があります。右側は歯車の線画で、インタラクションのイベント ハンドラが実行を開始するタイミングを示しています。中間のスペースは、中かっこ付きの入力遅延として示されます。
入力遅延のメカニズム。オペレーティング システムが入力を受け取ったら、操作を開始する前にブラウザに渡す必要があります。これには一定の時間がかかり、既存のメインスレッド処理によってさらに時間がかかります。

入力遅延の一部は避けられません。オペレーティング システムが入力イベントを認識してブラウザに渡すには、常に一定の時間がかかります。ただし、この部分の入力遅延はほとんど気付かれません。また、ページ自体で発生する他の事象によって、入力遅延が長くなり、問題が発生することもあります。

入力遅延について考える

一般的に、ユーザーのデバイスに関係なく、ウェブサイトがInteraction to Next Paint(INP)指標の「良好」な基準を満たせるように、インタラクションのすべての部分をできるだけ短くする必要があります。入力遅延を抑制することは、このしきい値を満たすための手段の一つにすぎません。

したがって、INP の「良好」な基準を満たすために、入力遅延をできるだけ短くする必要があります。ただし、入力遅延を完全に解消することはできません。ユーザーがページを操作しようとしているときに、メインスレッドの過剰な処理を回避している限り、入力遅延は問題を回避できる程度に低くする必要があります。

入力遅延を最小限に抑える方法

前述のように、入力遅延の一部は避けられませんが、一方で、入力遅延の一部は回避できます。入力の遅延が長い場合は、次の点を考慮してください。

過剰なメインスレッド処理を開始する繰り返しタイマーを回避する

JavaScript には、入力遅延に影響する可能性がある、よく使用される 2 つのタイマー関数(setTimeoutsetInterval)があります。2 つの違いは、setTimeout は指定した時間後に実行されるようにコールバックをスケジュールする点です。一方、setInterval は、n ミリ秒ごとに、または clearInterval でタイマーが停止するまで、コールバックを実行するようにスケジュールします。

setTimeout 自体に問題はありません。実際、長いタスクを回避するために役立つ場合があります。ただし、タイムアウトが発生するタイミングと、タイムアウト コールバックの実行時にユーザーがページの操作を試みるかどうかによって異なります。

また、setTimeout はループまたは再帰で実行できます。この場合、setInterval に似た動作をしますが、前の反復処理が完了するまで次の反復処理をスケジュールしないことをおすすめします。これにより、setTimeout が呼び出されるたびにループがメインスレッドに降伏しますが、コールバックで過剰な処理が行われないように注意する必要があります。

setInterval は一定の間隔でコールバックを実行するため、インタラクションの妨げになる可能性がはるかに高くなります。これは、setTimeout 呼び出しの単一インスタンス(ユーザー操作の妨げになる可能性がある 1 回限りのコールバック)とは異なり、setInterval は繰り返し実行されるため、ユーザー操作の妨げになる可能性が高いため、操作の入力遅延が増加するからです。

入力遅延を示す Chrome DevTools のパフォーマンス プロファイラのスクリーンショット。タイマー関数によってトリガーされるタスクは、ユーザーがクリック操作を開始する直前に発生します。ただし、タイマーによって入力遅延が長くなるため、操作のイベント コールバックが通常よりも遅れて実行されます。
Chrome DevTools のパフォーマンス パネルに表示されるように、入力遅延の原因となっている、以前の setInterval 呼び出しによって登録されたタイマー。入力遅延が追加されると、インタラクションのイベント コールバックが通常よりも遅れて実行されます。

タイマーがファーストパーティ コードで発生している場合は、タイマーを制御できます。必要かどうかを評価するか、作業をできるだけ減らすようにします。ただし、サードパーティ スクリプトのタイマーは別の話です。サードパーティ スクリプトの動作を制御できないことが多く、サードパーティ コードのパフォーマンスの問題を解決するには、関係者と連携して特定のサードパーティ スクリプトが必要なかどうかを判断し、必要な場合はサードパーティ スクリプト ベンダーに連絡して、ウェブサイトで発生する可能性のあるパフォーマンスの問題を解決するために何ができるかを判断する必要があります。

長時間のタスクを避ける

長い入力遅延を軽減する方法の一つは、長いタスクを避けることです。操作中にメインスレッドをブロックするメインスレッドの作業が多すぎると、長いタスクが完了する前に入力の遅延が増加します。

タスクが入力遅延を延長する時間の可視化。上図では、長時間のタスクの実行直後にインタラクションが発生しているため、入力が大幅に遅延し、イベント コールバックが想定よりも大幅に遅れて実行されています。基本的に、インタラクションはおおよそ同時に発生しますが、長いタスクは yield によって複数の小さなタスクに分割されるため、インタラクションのイベント コールバックをずっと早く実行できます。
タスクが長すぎてブラウザがインタラクションに十分に迅速に応答できない場合と、長いタスクを小さなタスクに分割した場合のインタラクションの変化を可視化したものです。

タスク内で行う処理の量を最小限に抑える(メインスレッドでできるだけ少ない処理を行うように常に努める必要があります)だけでなく、長いタスクを分割することで、ユーザー入力に対する応答性を改善できます。

インタラクションの重複に注意する

INP の最適化で特に難しいのは、重複するインタラクションがある場合です。インタラクションの重複とは、1 つの要素を操作した後、最初のインタラクションによる次のフレームのレンダリングが完了する前に、ページに対して別のインタラクションを行うことを意味します。

タスクが重なり、入力遅延が長くなる場合の例。この図では、クリック操作が keydown 操作と重なり、keydown 操作の入力遅延が増加しています。
Chrome の DevTools のパフォーマンス プロファイラで 2 つの同時インタラクションの可視化。最初のクリック操作でのレンダリング処理により、その後のキーボード操作の入力が遅延します。

インタラクションの重複の原因は、ユーザーが短時間に多くのインタラクションを行うという単純なものである可能性があります。これは、ユーザーがフォーム フィールドに入力する際に発生することがあります。この場合、非常に短い時間に多くのキーボード操作が行われる可能性があります。キーイベントの処理が特に負荷が高い場合(バックエンドにネットワーク リクエストを送信する自動入力フィールドの一般的なケースなど)は、次の 2 つの方法があります。

  • 入力をデバウンスして、特定の期間にイベント コールバックが実行される回数を制限することを検討してください。
  • AbortController を使用して送信 fetch リクエストをキャンセルし、fetch コールバックの処理でメインスレッドが輻輳しないようにします。注: AbortController インスタンスの signal プロパティを使用してイベントを中止することもできます。

インタラクションの重複による入力遅延の原因として、負荷の高いアニメーションも考えられます。特に、JavaScript のアニメーションでは多くの requestAnimationFrame 呼び出しが発生し、ユーザー操作の妨げになる可能性があります。この問題を回避するには、可能な限り CSS アニメーションを使用して、費用のかかるアニメーション フレームのキューイングを回避します。ただし、この方法を使用する場合は、合成されていないアニメーションを避け、アニメーションがメインスレッドではなく、主に GPU とコンポーザ スレッドで実行されるようにしてください。

まとめ

入力遅延はインタラクションの実行にかかる時間の大部分を占めるわけではありませんが、インタラクションのどの部分でも時間を短縮できることを理解することが重要です。入力遅延が長い場合は、遅延を短縮する方法があります。タイマーのコールバックを繰り返さないこと、長いタスクを分割すること、インタラクションの重複に注意することは、入力遅延を減らし、ウェブサイトのユーザーがより速くインタラクションできるようにするのに役立ちます。

Unsplash のヒーロー画像(Erik Mclean 撮影)。