入力遅延を最適化する

入力遅延の概要と、入力遅延を短縮してインタラクティビティを高速化する方法をご紹介します。

ウェブでのやり取りは複雑なものであり、ブラウザ内でさまざまな行動が引き起こされます。ただし、これらに共通するのは、イベント コールバックの実行が開始されるまでに入力遅延が生じることです。このガイドでは、入力遅延の概要と、入力遅延を最小限に抑えることで、ウェブサイトのインタラクションを高速化する方法について説明します。

入力遅延とは

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

<ph type="x-smartling-placeholder">
</ph> 入力遅延の簡略化した可視化。左側にはマウスのカーソルの線画があり、その後ろにスターバーストが描かれており、操作の開始を表しています。右側は歯車の線画で、インタラクションのイベント ハンドラの実行が開始されるタイミングを表しています。中間のスペースは、中かっこで入力遅延として示されます。 <ph type="x-smartling-placeholder">
</ph> 入力遅延の背後にある仕組み。入力は、オペレーティング システムで受け取ったら、インタラクションを開始する前にブラウザに渡す必要があります。これにはある程度の時間がかかりますが、既存のメインスレッドの処理によって長くなる可能性があります。

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

入力遅延の考え方

一般的には、ウェブサイトでインタラクションのあらゆる部分を可能な限り短くして、Interaction to Next Paint(INP)指標の「良好」] を満たす可能性が高くなります。しきい値は、ユーザーのデバイスに関係ありません入力遅延の抑制は、このしきい値を満たすためのほんの一部にすぎません。

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

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

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

メインスレッドの過度な処理を発生させる繰り返しのタイマーを避ける

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

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

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

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

<ph type="x-smartling-placeholder">
</ph> 入力遅延を示す Chrome DevTools のパフォーマンス プロファイラのスクリーンショット。タイマー関数によって実行されるタスクは、ユーザーがクリック操作を開始する直前に発生します。ただし、タイマーにより入力遅延が延長されるため、インタラクションのイベント コールバックの実行が本来より遅くなります。 <ph type="x-smartling-placeholder">
</ph> Chrome DevTools のパフォーマンス パネルに表示されている、前回の setInterval 呼び出しによって登録されたタイマーが入力遅延の要因になっている。追加の入力遅延により、インタラクションのイベント コールバックが本来より遅く実行されることになります。

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

長いタスクを避ける

長い入力遅延を軽減する方法の一つは、長いタスクを避けることです。インタラクション中にメインスレッドをブロックするような過剰なメインスレッドの処理があると、長いタスクが完了する前にメインスレッドの入力遅延が増加します。

<ph type="x-smartling-placeholder">
</ph> タスクが入力遅延を延長する時間を示す可視化。上の図では、単一の長いタスクの実行の直後にインタラクションが発生しています。そのため、入力に大幅な遅延が生じ、イベント コールバックが想定よりも大幅に遅れて実行されています。一番下では、ほぼ同時にインタラクションが発生しますが、長いタスクは yield によっていくつかの小さなタスクに分割されるため、インタラクションのイベント コールバックをより迅速に実行できます。 <ph type="x-smartling-placeholder">
</ph> タスクが長すぎて、ブラウザがインタラクションにすばやく反応しない場合と、長いタスクが小さなタスクに分割された場合のインタラクションについて、何が起こるかを可視化した図。

常にメインスレッドで行う作業量を最小限に抑えるだけでなく、長いタスクを分割してユーザー入力に対する応答性を高めることもできます。

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

複数のインタラクションが重複している場合は INP の最適化が特に難しくなります。インタラクションの重複とは、ある要素を操作した後、最初のインタラクションが次のフレームを表示する前に、ページで別のインタラクションを行うことを意味します。

<ph type="x-smartling-placeholder">
</ph> タスクが重複して長い入力遅延を発生させる場合の図。この図では、クリック操作がキーダウン操作と重なって、キーダウン操作の入力遅延が長くなっています。 <ph type="x-smartling-placeholder">
</ph> Chrome の DevTools のパフォーマンス プロファイラで、2 つの同時インタラクションを可視化。最初のクリック操作でのレンダリング処理により、後続のキーボード操作で入力遅延が発生します。

インタラクションの重複の原因としては、ユーザーが短期間に多くのインタラクションを行うような単純なケースもあります。これは、ユーザーがフォームのフィールドに入力している場合に発生することがあります。これは、非常に短い期間で、多くのキーボード操作が行われる場合です。キーイベントの作業に特にコストがかかる場合(ネットワーク リクエストがバックエンドに対して行われる予測入力フィールドの一般的なケースなど)は、次の 2 つのオプションがあります。

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

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

まとめ

入力の遅延は、インタラクションの実行にかかる時間の大部分を占めているわけではありませんが、インタラクションのあらゆる部分に時間がかかることを理解しておくことが重要です。入力遅延が長い場合は、遅延を短縮できます。タイマー コールバックの繰り返しを回避したり、長いタスクを分割したり、インタラクションの重複に注意したりすることは、入力遅延の短縮につながり、ウェブサイトのユーザーのインタラクティビティを高速化することができます。

Unsplash のヒーロー画像(Erik Mclean