【実務・中級編】 ブラウザメインスレッドのタスク管理 – Webブラウザの仕組み実践ガイド

やあ。今日もブラウザのメインスレッドと格闘しているか?

「なぜか画面がカクつく」「インタラクションが重い」——フロントエンドエンジニアが避けて通れないこの泥沼。その正体は、ブラウザの心臓部である「メインスレッド」の交通整理に失敗していることがほとんどだ。

今日は、ブラウザが裏側でどうタスクを捌いているのか、その「現場のリアル」を解剖していこう。教科書的な話は適当に読み飛ばして、俺たちが明日からコードを書く時に意識すべき「脳内OS」をインストールするつもりで読んでくれ。

—

1. メインスレッドは「孤独な料理人」だ

ブラウザのメインスレッドを一つの厨房だと想像してほしい。そこで働くのはたった一人のシェフだ。彼がやっていることは山積みだ。

  • HTMLのパースとDOM構築
  • CSSOMの構築とスタイルの計算
  • レイアウト(配置の計算)とペイント(描画)
  • JavaScriptの実行
  • ユーザーイベント(クリックやスクロール)の処理

これら全てを、あの孤独なシェフが「タスクキュー」から一つずつ取り出してこなしている。JavaScriptで重い処理を走らせるということは、シェフに「この100万回ループが終わるまで、客(ユーザー)の注文には一切答えるな」と命令しているのと同じなんだ。これが「メインスレッドのブロック」の正体だ。

—

2. タスク管理の裏側:マクロとマイクロのせめぎ合い

ブラウザのタスク管理は、単純なFIFO(先入れ先出し)じゃない。ここには「階層」がある。

1. マクロタスク (Task Queue): `setTimeout`, `setInterval`, DOMイベント、レンダリング処理など。
2. マイクロタスク (Microtask Queue): `Promise.then`, `queueMicrotask`, `MutationObserver`など。

重要なルールはこれだ:
「一つのマクロタスクが終わるたびに、マイクロタスクキューが空になるまで全てを処理する」。

もし、JavaScriptで無限にマイクロタスクを生成したらどうなるか? メインスレッドは永久にマイクロタスクを処理し続け、画面の描画(レンダリング)に一生戻ってこれない。これが「ブラウザが固まる」という現象の、技術的に最も美しい(そして残酷な)理由だ。

—

3. 実践:重い処理を「分割」してメインスレッドを解放する

現場でよくあるミスは、大量のデータを一気に処理しようとすることだ。例えば、1万件のアイテムをレンダリングする時、`forEach`で一気にDOMを叩けば、メインスレッドは完全に沈黙する。

ここで、`requestIdleCallback`や`setTimeout`を使った「タスクの分割(Time Slicing)」というテクニックが活きてくる。

サンプルコード:メインスレッドを殺さないリスト生成

/

  • 大量のデータをメインスレッドをブロックせずに処理する関数
  • @param {Array} items – 処理対象のデータリスト
  • @param {Function} processFn – 個別の要素に対する処理

/
function processLargeData(items, processFn) {
let index = 0;

function chunkProcessor(deadline) {
// ブラウザが暇な時間(deadline.timeRemaining)がある間だけ処理を続ける
while (index < items.length && deadline.timeRemaining() > 0) {
processFn(items[index]);
index++;
}

if (index < items.length) { // まだ残っていたら、次のフレーム(ブラウザの空き時間)を待つ requestIdleCallback(chunkProcessor); } else { console.log("処理完了!メインスレッドは一度も死んでいないはずだ。"); } } // 処理開始 requestIdleCallback(chunkProcessor); } // 使い方 const data = Array.from({ length: 10000 }, (_, i) => i);
processLargeData(data, (item) => {
// ここでDOM操作や計算を行う
const el = document.createElement(‘div’);
el.textContent = `アイテム ${item}`;
document.body.appendChild(el);
});

—

4. シニアからのアドバイス:なぜ「最適化」が必要なのか

このコードのポイントは、「ブラウザの機嫌を伺っている」ことだ。`requestIdleCallback`を使うことで、「今、描画の優先順位が高いか?」「シェフは暇か?」をブラウザ自身に判断させている。

現場で「パフォーマンスが悪い」と悩んだ時、まずはChromeの「Performanceタブ」を開いてくれ。

  • 長いバー(Long Task)が立っていたら、それはシェフが一つ作業に夢中になりすぎて、他の客を無視している証拠だ。
  • そのバーを切り刻んで、タスクを小さく分割する。これができるかどうかが、ジュニアとシニアの境界線だ。

最後に

ブラウザは魔法じゃない。極めて論理的で、かつ非情なシングルスレッドの実行環境だ。
俺たちが書くコードは、この孤独なシェフに「いかに効率よく、かつユーザーを待たせずに注文を捌いてもらうか」というパズルなんだ。

「とりあえず動けばいい」コードから卒業して、メインスレッドの呼吸を感じ取れるようになれば、君のプロダクトのUXは劇的に変わる。

分からないことがあればいつでも聞いてくれ。現場からは以上だ。

コメント

タイトルとURLをコピーしました