ブラウザの「メインスレッド」を制するものは、Webのパフォーマンスを制す
フロントエンドの現場で、「なぜかUIがカクつく」「イベントハンドラが期待通りに動かない」といった壁にぶつかったことはないだろうか?
多くのエンジニアが「ブラウザはコードを上から順に実行している」という程度の認識で開発を続けているが、中級から先へ突き抜けるには、ブラウザの心臓部である「メインスレッド」のタスク処理の流儀を骨の髄まで理解する必要がある。
今日は、ブラウザのレンダリングパイプラインと、そこでのタスクの優先順位について、現場の知見を交えて深掘りしていく。
—
1. メインスレッドは「孤独な料理人」である
まず大前提として、ブラウザのメインスレッドはシングルスレッドだ。JavaScriptの実行、DOMのパース、CSSOMの構築、レイアウトの計算、ペイント……これらすべてが、たった一人の「料理人」によって順番に処理されている。
ここで重要なのがイベントループという仕組みだ。料理人(メインスレッド)は、次々に届く「注文(タスク)」をキューから取り出しては捌き、また次の注文を見る。この際、注文の優先順位がパフォーマンスを大きく左右する。
2. 「マクロ」と「マイクロ」の支配関係
現場で最も混乱を招くのが、タスクの優先順位だ。以下の3つのキューが、どんな順番で消化されるかを暗記レベルで叩き込んでおこう。
1. マイクロタスクキュー: 優先度最高。`Promise.then`, `MutationObserver` など。
2. レンダリングパイプライン: DOMの更新やレイアウト計算。
3. マクロタスクキュー: `setTimeout`, `setInterval`, `I/O`, UIイベントなど。
重要なルールはこれだ:
「メインスレッドは、現在実行中のスタックが空になると、まずマイクロタスクキューを『すべて』空にする。その後、必要であればレンダリングを行い、最後にマクロタスクキューから『1つだけ』を取り出して処理する」
この「マイクロタスクをすべて消化する」という挙動こそが、無限ループでブラウザをクラッシュさせる原因にもなれば、逆に非同期処理を極めて高速に実行する鍵にもなる。
—
3. 実践:ブラウザの挙動を可視化するコード
以下のコードをブラウザのコンソールに貼り付けてみてほしい。ブラウザが裏側でどう優先順位を判断しているか、その一端が垣間見えるはずだ。
console.log(‘1. 同期コードの開始’);
// マクロタスク
setTimeout(() => {
console.log(‘4. setTimeout (マクロタスク)’);
}, 0);
// マイクロタスク
Promise.resolve().then(() => {
console.log(‘3. Promise (マイクロタスク)’);
});
// 同期コード
console.log(‘2. 同期コードの終了’);
/
- 実行結果の解説:
- 1 -> 2 -> 3 -> 4 の順で表示される。
- 「同期処理が終わる」→「マイクロタスクを全て消化」→「マクロタスクを1つ消化」
- というのがブラウザの鉄の掟である。
/
—
4. 現場で意識すべき「UIブロッキング」回避の極意
中級エンジニアがよく犯すミスは、メインスレッドで重い計算(数万件のデータ処理など)を行い、画面をフリーズさせることだ。
もし君が大規模なデータ処理を任されたら、以下の手法を思い出してほしい。
- `requestAnimationFrame` (rAF) の活用:
画面の描画更新の直前に処理を割り込ませる。アニメーションを滑らかにしたい場合は必須だ。
- タスクの分割 (Time Slicing):
大きな処理を `setTimeout` や `setImmediate` で細切れに分割し、その合間に「レンダリングの時間」をブラウザに与える。
// 重い処理を分割して実行する例
function processHeavyData(data) {
const CHUNK_SIZE = 100;
let index = 0;
function next() {
// 100個ずつ処理してメインスレッドを開放する
const end = Math.min(index + CHUNK_SIZE, data.length);
for (; index < end; index++) {
// 何らかの重い計算処理
}
if (index < data.length) {
// 次のフレームまで待機してから再開
setTimeout(next, 0);
}
}
next();
}
5. 最後に:スペシャリストへの道
ブラウザは君の書いたコードを「何とかして動かそう」と必死に頑張っている。しかし、その頑張りを無駄にするか、最大化するかは君の書き方次第だ。
「なぜ動くのか」だけでなく、「ブラウザのメインスレッドが今、何を待っているのか」を想像しながらコードを書いてほしい。それができるようになった時、君が書くアプリケーションのUIは、驚くほど滑らかで信頼性の高いものに変わっているはずだ。
何か具体的なパフォーマンスのボトルネックに悩んでいたら、いつでも相談してくれ。技術の深淵を一緒に覗きに行こう。

コメント