ブラウザの「メインスレッド」という名の戦場:レンダリングとタスクスケジューリングの深淵
フロントエンド開発の現場で、重いJavaScriptを実行した瞬間に画面がカクついた経験、誰もがあるだろう?「なぜ動かないのか」と悩む前に、ブラウザが裏側で繰り広げている壮絶なタスクの優先順位争いを理解しておく必要がある。
今日は、ブラウザの心臓部である「メインスレッド」がどうやって日々のお仕事(タスク)を捌いているのか、その泥臭い実態について話そう。
1. メインスレッドは「たった一人」の司令塔だ
まず大前提として、ブラウザのメインスレッドは「シングルスレッド」で動いている。これは、一度に一つしか作業ができないという制約だ。
ここには、以下のようなタスクが押し寄せてくる。
- JavaScriptの実行(あなたの書いたコード)
- HTMLのパースとDOM構築
- スタイル計算(Recalculate Style)
- レイアウト計算(リフロー)
- 描画(リペイント)
これらが一つの列(タスクキュー)に並んで、順番待ちをしている。もし誰かが「重い計算」で列を塞いでしまったら? それが、画面がフリーズする原因だ。
2. ブラウザの優先順位付け:なぜ「アニメーション」は滑らかなのか
ブラウザは実は非常に賢い。タスクを単に「先着順」で処理するのではなく、「人間が心地よいと感じる体験」を最優先するように設計されている。
通常、ブラウザは16.6ms(60fps)ごとに画面を更新しようと試みる。そのサイクルの直前に、ブラウザは「タスクキュー」を確認する。
1. マイクロタスク(Promiseなど):今のタスクが終わったら即座に実行する(優先度:極高)
2. レンダリング関連(リフロー/リペイント):フレームの切れ目で処理する(優先度:高)
3. マクロタスク(setTimeoutなど):空いた時間で順次処理する(優先度:低)
この「フレームの切れ目」にレンダリングをねじ込む能力こそが、今のWebが滑らかに動いている理由なんだ。
3. 実践:メインスレッドをブロックしないための技術
では、現場でどうやってこの「渋滞」を避けるか。答えは「タスクを切り刻むこと」だ。
非推奨:メインスレッドを殺す書き方
// 数百万回のループ。メインスレッドはここで数秒間「死ぬ」
function heavyTask() {
for (let i = 0; i < 1000000000; i++) {
// 重い計算...
}
console.log("終わった!けど、その間ブラウザはフリーズしてるよ。");
}
推奨:タスクを分割して「息継ぎ」させる
`setTimeout` や `requestAnimationFrame` を使って、ブラウザに「次回のフレームまで待つ」余地を与えるのがプロのやり方だ。
/
- 大きなタスクを分割して、ブラウザのメインスレッドに「息継ぎ」をさせる手法
/
async function processLargeArray(items) {
const chunkSize = 1000; // 一度に処理する量
for (let i = 0; i < items.length; i += chunkSize) { // 処理の塊 const chunk = items.slice(i, i + chunkSize); chunk.forEach(item => { / …重い処理… / });
// ここが重要!
// 次のタスク開始までメインスレッドを解放する
await new Promise(resolve => setTimeout(resolve, 0));
// このawaitの隙間に、ブラウザは「レンダリング」や「ユーザー入力」を割り込ませる
}
}
4. `requestIdleCallback` という選択肢
もし、処理が「今すぐでなくてもいい」ものなら、`requestIdleCallback` を使え。これは「ブラウザが暇なときを見計らって実行してね」とお願いするAPIだ。
// ブラウザが暇なときだけログを送信したり、分析処理を行う
function sendAnalytics(deadline) {
// deadline.timeRemaining() で、あと何ミリ秒暇かを確認できる
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
doTask(tasks.pop());
}
if (tasks.length > 0) {
requestIdleCallback(sendAnalytics); // まだ残っていれば次回の暇な時間に
}
}
requestIdleCallback(sendAnalytics);
最後に:シニアからのアドバイス
「パフォーマンスを最適化する」ということは、ブラウザの機嫌を損ねないように、上手にタスクをパス回ししてあげることと同義だ。
1. DOM操作を最小限にせよ(リフローのコストは高い)
2. 重い処理は切り刻んで、ブラウザに休憩を与えよ
3. 計測を怠るな(Chrome DevToolsのPerformanceタブは、君の書いたコードの「心電図」だ)
綺麗なコードを書くのも大事だが、こうして「ブラウザがどう動いているか」という物理的な制約を意識し始めたとき、君は中級エンジニアから一歩先の「アーキテクト」へと近づくはずだ。
さあ、エディタに戻って、少しだけ優しくメインスレッドを扱ってやってくれ。

コメント