入力遅延を最適化する

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

Jeremy Wagner 氏
Jeremy Wagner

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

入力遅延とは何ですか?

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

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

入力遅延の一部は回避できません。オペレーティング システムが入力イベントを認識してブラウザに渡すまでに、常にある程度の時間がかかります。しかし、入力遅延のこの部分は多くの場合気づくことすらなく、ページ自体で発生する他の原因で入力遅延が問題を引き起こすことがあります。

入力遅延についての考え方

一般的に、ユーザーのデバイスに関係なく、ウェブサイトが Interaction to Next Paint(INP)指標の「良好」しきい値を満たす可能性を最大限に高めるには、インタラクションのあらゆる部分をできるだけ短くする必要があります。入力遅延をチェックできるようにすることは、しきい値を満たす方法の 1 つにすぎません。

入力遅延の許容範囲を決めるために、初回入力遅延(FID)のしきい値に注目したくなるかもしれませんが、FID の「良好」なしきい値は 100 ミリ秒以下です。このしきい値を超えると、INP の予算の半分が遅延のみの入力に割り当てられ、インタラクションによってイベント コールバックの実行やブラウザが次のフレームを描画する時間もかかることを考慮すると、この方法はおすすめできません。

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

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

前述のように、一定の入力遅延は回避できませんが、入力遅延は回避できます。長い入力遅延に苦労している場合は、以下の点を考慮してください。

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

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

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

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

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

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

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

時間のかかるタスクを避ける

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

タスクが入力遅延をどのくらい延長しているかを可視化したもの。一番上では、長時間のタスクが 1 つ実行された直後に操作が発生するため、入力が大幅に遅延し、イベント コールバックが想定よりもはるかに遅れて実行されます。一番下では、インタラクションがほぼ同時に発生しますが、長いタスクは、放棄によって複数の小さなタスクに分割されるため、インタラクションのイベント コールバックがより早く実行されます。
タスクが長すぎてブラウザがインタラクションに迅速に応答できない場合と、長いタスクが小さなタスクに分割された場合とで、インタラクションがどうなるかの可視化。

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

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

INP の最適化で特に困難なのは、重複するインタラクションがある場合です。インタラクションの重複とは、ユーザーが 1 つの要素を操作した後、最初のインタラクションで次のフレームがレンダリングされる前に別のインタラクションを行うことを指します。

長い入力遅延を生成するためにタスクがオーバーラップする状況の図。この図では、クリック操作と Keydown 操作が重なり、キーダウン操作の入力遅延が長くなっています。
Chrome の DevTools のパフォーマンス プロファイラで、2 つの同時インタラクションを可視化した図。最初のクリック操作でのレンダリング処理により、後続のキーボード操作で入力遅延が発生します。

インタラクションのソースの重複は、ユーザーが短期間に多数のインタラクションを行っているような単純なものです。これは、ユーザーがフォーム フィールドに入力する際に発生することがあります。これらのフィールドでは、キーボード操作の多くが短時間で行われる可能性があります。バックエンドにネットワーク リクエストを行うオートコンプリート フィールドの一般的なケースなど、キーイベントの処理が特に高コストである場合は、次の 2 つの方法があります。

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

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

まとめ

入力の遅延がインタラクションの実行にかかる時間の大部分を占めているとは限りませんが、インタラクションのあらゆる部分に費やされる時間は短縮可能であることを理解しておくことが重要です。入力遅延が長い場合は、短縮できる方法があります。タイマーによるコールバックの繰り返しや、時間のかかるタスクを分割し、操作が重なる可能性に注意することは、入力の遅延を減らし、ウェブサイトのユーザーのインタラクティビティを高速化するのに役立ちます。

Unsplash のヒーロー画像(Erik Mclean 提供)