INP改善のためにメインスレッドへyieldする方法
scheduler.yield()を使用して長いタスクを分割し、ページの応答性を維持する

メインスレッドへyieldする
ロマンチックな映画を想像してほしい。舞台は小さな村の中心にある、フランスの小さな市場だ。通りは、コーヒーを飲み、クロワッサンを食べ、花を買うカップルで溢れている。ここで、一度に一人の買い手しか売り手から物を購入できず、他のすべての人は順番を待たなければならないとしたらどうなるか想像してみてほしい。パン屋は注文に追われ、花屋では口論が起き、ロマンチックな散歩はイライラする待ち時間に変わってしまうだろう。
実は……これと似たようなことが、処理が集中しすぎたウェブサイトでも起こっているのだ。
最終レビュー: Arjen Karel (2026年3月)
INPにおいてyieldingが重要である理由
ブラウザのメインスレッドは、HTMLとCSSの解析、JavaScriptの実行、クリックやスクロールといった入力イベントの処理、視覚的なレンダリングなど、重要なプロセスをすべて処理する。シングルスレッドモデルで動作するため、一度に実行できるタスクは1つだけである。タスクが実行を開始すると、ブラウザは完了するまでそれを実行し、終わるまで停止しない。現在のタスクが終了するまで、他のタスクはスケジュールされない。これをメインスレッドのブロックと呼ぶ。
メインスレッドのブロックは、Interaction to Next Paint (INP)スコアが悪化する主な原因である。訪問者がボタンをクリックし、JavaScriptがメインスレッドを200ミリ秒ブロックした場合、スクリプトが終了するまでブラウザは応答をレンダリングできない。INPの処理時間フェーズは、まさにこの遅延を測定している。2025 Web Almanacによると、モバイルのTotal Blocking Timeの中央値は1,916ミリ秒で、前年から58%増加している。これは、ブラウザがユーザー入力に応答できない時間が2秒近くあることを意味する。
この問題を解決する方法の1つが、メインスレッドへyieldすることである。yieldingは、長いタスクを複数の小さなタスクに分割し、その間にブラウザがユーザー入力への応答など、より重要な処理を行えるようにするテクニックである。
長いタスクとブロック時間: タスクの処理に50ミリ秒以上かかった場合、それは長いタスク(Long Task)として分類され、50ミリ秒のしきい値を超えた部分はタスクの「ブロック時間」と呼ばれる。これらの長いタスクを小さなチャンクに分割することで、計算量の多い操作を処理している間でも、ブラウザは応答性を維持できるようになる。
従来のyielding戦略
Prioritized Task Scheduling APIが登場する前は、メインスレッドへyieldする方法が4つあったが、いずれも限界がある。
- setTimeout(): 最も一般的な戦略。遅延0の
setTimeout()はタスクをキューの末尾に追加し、他のタスクを先に実行できるようにする。問題点として、タスクはキューの末尾にしか追加できないため、コードが再開する前に他のスクリプトが割り込む可能性がある。また、setTimeout()の呼び出しをネストすると、5回目の呼び出し以降は最小5ミリ秒の遅延が強制される。 - requestAnimationFrame(): ブラウザの次回の再描画の前に実行する関数をキューに入れる。次のレイアウト更新後にコールバックが確実にスケジュールされるように、
setTimeout()と組み合わせて使用されることが多い。yieldingのために設計されたものではなく、アニメーション処理のために設計されている。 - requestIdleCallback(): ブラウザのアイドル時間中に実行可能な、重要度が低く優先順位の低いタスクに最適である。限界として、スケジュールされたタスクが、処理が集中しているメインスレッド上で迅速に(あるいは全く)実行される保証はない。
- isInputPending(): 保留中のユーザー入力をチェックし、入力が検出された場合にのみyieldする。Googleはもはやこのアプローチを推奨していない。偽陰性を返す可能性があり、アニメーションやレンダリングの更新など、他のパフォーマンスに不可欠な処理を考慮していないためである。
scheduler.yield()
これらのメソッドの限界は、多くのサイトがINPの基準を満たしていない現状において、Chromeチームにとっての懸念事項であった。これに対処するため、彼らはscheduler.yield()を構築した。これは、タスクの順序を再配置したり複雑さを加えたりすることなく、開発者がメインスレッドへ直ちにyieldできるようにする新しいAPIである。
scheduler.yield()はChrome 129(2024年9月)で安定版としてリリースされ、現在ではSafariを除くすべての主要ブラウザでサポートされている。
コード例
Safariはまだscheduler.yield()をサポートしていないため、setTimeout()へのfallbackを使用する。
function yieldToMain() {
if ('scheduler' in window && 'yield' in window.scheduler) {
return window.scheduler.yield();
}
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
} yieldToMain()関数は、window.scheduler.yieldが存在するかどうかを確認する。存在する場合はネイティブAPIを使用し、そうでない場合はsetTimeout()にfallbackする。これはGoogleが推奨するパターンと一致している。
完全なPrioritized Task Scheduling API(scheduler.postTask()やTaskControllerを含む)を必要とするプロジェクト向けに、Google Chrome Labsは公式のポリフィルを保守している。
実用例:yieldToMain()を使用した検索機能の強化
yieldToMain()を使用して、ユーザーの検索体験を向上させる方法を紹介する。
handleSearch()関数は、まずボタンのコンテンツを更新して検索が進行中であることを即座にフィードバックし、その後yieldしてブラウザにその更新をレンダリングさせる。次に、fetchData()が検索データを取得し、updateHTML(data)が結果を表示する。再度yieldToMain()を実行することで、レイアウトの迅速な更新を保証する。最後に、それほど重要ではないタスクがブラウザのアイドル時間中にスケジュールされる。requestIdleCallback()の前ではyieldしていないことに注意してほしい。これは、メインスレッドがアイドル状態のときにのみ実行されるためである。
async function handleSearch() {
/* 送信後、ボタンのコンテンツをすばやく更新する */
updateButtonToPending();
/* メインスレッドへyieldする */
await yieldToMain();
/* データを取得してHTMLを更新する */
const data = await fetchData();
updateHTML(data);
/* 再びメインスレッドへyieldする */
await yieldToMain();
/* ブラウザのアイドル時間中にのみ実行すべき一部の関数 */
requestIdleCallback(sendDataToAnalytics);
} 別の実践的な例として、分析スクリプトがインタラクションをブロックするのを防ぐために、dataLayerのpushでこの同じyieldパターンを使用する方法も参照してほしい。
scheduler.yield()が優れている理由
遅延タスクをタスクキューの末尾に追加するsetTimeout()とは異なり、scheduler.yield()は実行を一時停止し、その続きをキューの先頭に配置する。優先度の高い処理(入力コールバックの処理など)が完了するとすぐにコードが再開される。これが重要な違いである。つまり、コードが再開する前に他のスクリプトが割り込むリスクなしに、メインスレッドへyieldできるのだ。

また、scheduler.yield()はPrioritized Task Scheduling APIと連携するように設計されている。scheduler.postTask()のコールバック内で呼び出された場合、scheduler.yield()はタスクの優先度レベルを継承する。この組み合わせにより、JavaScriptの優先順位付けとyieldのタイミングをきめ細かく制御できるようになる。
ブラウザのサポート状況
scheduler.yield()は、世界中のブラウザの71.5%でサポートされている。
- Chrome 129+ および Edge 129+: 2024年9月から安定版
- Firefox 142+: 2025年8月から安定版
- Safari: サポートなし、今後の予定も未発表
上記のyieldToMain()関数にあるsetTimeout()のfallbackにより、すべてのブラウザでコードが機能することが保証される。Safariユーザーには続きの処理がキューの末尾に回る古い挙動が適用されるが、ページは引き続きyieldする。ブラウザのサポートが拡大するにつれて、より多くのユーザーが自動的に高速な再開の恩恵を受けられるようになる。
もしサードパーティのスクリプトがメインスレッドを完全にブロックしていることが問題であれば、yieldingよりもそれらのスクリプトを遅延させることが、より良い最初のステップである。yieldingは、自分自身のコードが多くの処理を行う必要があるものの、チャンク間でブラウザの応答性を維持したい場合に役立つ。
実際の環境でyieldingがINPを改善していることを確認するには、Real User Monitoringを使用してページを監視しよう。LighthouseのようなラボツールはTotal Blocking Timeを測定するが、訪問者が経験する実際のINPはフィールドデータからしか把握できない。

