Scheduler API実践:ブラウザタスク優先度スケジューリングとメインスレッド最適化

性能优化(更新: 2026年6月12日)

メインスレードスケジューリングのジレンマ

ブラウザのメインスレッドはUIレンダリング、イベント処理、JavaScript実行を同時に担います。長時間タスク(>50ms)はインタラクションをブロックし、INP指標を悪化させます:

従来手法 問題
setTimeout(fn, 0) 実際の遅延4ms、優先度制御不可
requestIdleCallback アイドル時のみ実行、時効性保証なし
await scheduler.yield() ✅ 精密な譲渡、スケジューリング順序を維持
scheduler.postTask() 優先度 + 譲渡 + キャンセル = 完全なスケジューリング

Scheduler API優先度モデル

scheduler.postTask()は3段階の優先度を提供し、ブラウザ内部スケジューリングにマッピングされます:

優先度 意味 典型的用途 Chrome内部マッピング
user-blocking 最高、即時実行 ユーザー入力応答、アニメーション Very High
user-visible 中程度、早期実行 DOM更新、データ解析 High / Medium
background 最低、アイドル時実行 ログ送信、事前計算 Low / Best Effort

基本的な使い方

const result = await scheduler.postTask(
  () => heavyComputation(data),
  { priority: 'background' }
);

実践:画像圧縮タスクの優先度スケジューリング

ToolsKuの画像圧縮はバッチ画像処理中もユーザー操作に応答する必要があります:

async function handleBatchCompress(files: File[]) {
  const controller = new TaskController({ priority: 'background' });

  for (const file of files) {
    scheduler.postTask(
      async () => {
        const result = await compressImage(file, { quality: 0.85 });
        await scheduler.postTask(
          () => updateProgressUI(file.name, result),
          { priority: 'user-visible' }
        );
      },
      { signal: controller.signal }
    );
  }

  return controller;
}

ユーザーが「キャンセル」をクリックすると、controller.abort()が保留中の全タスクを即座に停止します。


scheduler.yield():精密なメインスレッド譲渡

scheduler.yield()setTimeout(fn, 0)より精密です。より高優先度のタスクに譲渡しつつ、現在のタスクの実行順序を維持します:

async function processLargeDataset(items: Item[]) {
  const results: Result[] = [];

  for (let i = 0; i < items.length; i++) {
    results.push(transform(items[i]));

    if (i % 50 === 0) {
      await scheduler.yield();
    }
  }

  return results;
}

yield() vs setTimeout()の比較

特性 setTimeout(fn, 0) scheduler.yield()
最小遅延 4ms 0ms
優先度認識 ❌ なし ✅ 高優先度に譲渡
実行順序 保証なし ✅ スケジューリング順序を維持
長時間タスク分割
ブラウザサポート 全部 Chrome 115+

TaskController:キャンセルと動的優先度

const controller = new TaskController({ priority: 'user-visible' });

scheduler.postTask(() => renderChart(data), { signal: controller.signal });

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    controller.setPriority('background');
  } else {
    controller.setPriority('user-visible');
  }
});

controller.abort();

長時間タスク回避の戦略組み合わせ

戦略1:チャンク + yield

async function chunkedProcess<T>(items: T[], fn: (item: T) => void, chunkSize = 20) {
  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    chunk.forEach(fn);
    await scheduler.yield();
  }
}

戦略2:優先度レイヤリング

function scheduleWork(work: WorkItem) {
  const priority = work.type === 'user-input' ? 'user-blocking'
    : work.type === 'render' ? 'user-visible'
    : 'background';

  return scheduler.postTask(work.execute, { priority });
}

戦略3:AbortController統合

const ac = new AbortController();

scheduler.postTask(() => mergePDFs(files), {
  priority: 'background',
  signal: ac.signal
});

window.addEventListener('beforeunload', () => ac.abort());

パフォーマンス実測:PDF結合シナリオ

PDF結合で20個のPDFファイルを処理したベンチマーク:

手法 総所要時間 INP (ms) メインスレッドブロック
同期ループ 3.2s 480 3.2s
setTimeoutチャンク 3.3s 120 0.4s
postTask + yield 3.2s 60 0.1s

総所要時間は変わらず、INPは480msから60msに低下—インタラクション応答性が8倍改善。


互換性とフォールバック

async function yieldToMain() {
  if ('scheduler' in window && 'yield' in scheduler) {
    await scheduler.yield();
  } else {
    return new Promise(resolve => setTimeout(resolve, 0));
  }
}

function postTask(fn: () => void, options?: { priority?: string }) {
  if ('scheduler' in window) {
    return scheduler.postTask(fn, options as any);
  }
  return Promise.resolve(fn());
}

よくある質問

postTaskとrequestIdleCallbackのどちらを選ぶか?

requestIdleCallbackはブラウザがアイドル時のみ実行—全く緊急でない作業(プリロード等)に適しています。postTaskbackground優先度は類似していますが、より制御可能でキャンセルや動的優先度調整をサポートします。

yield()は総所要時間を増やすか?

単一のyield()のコストは約0.1ms。50項目ごとにyieldして10000項目を処理する場合、追加オーバーヘッドはわずか20msで、回避したブロック時間より遥かに小さいです。

優先度のデバッグ方法?

Chrome DevTools → Performanceパネルでタスクに優先度情報が表示されます。scheduler.postTask(() => console.trace(), { priority: 'background' })でスケジューリングをトレースすることも可能です。


まとめ

Scheduler APIはブラウザメインスレッドに精密な優先度スケジューリング能力を提供します。scheduler.postTask()は3段階優先度のタスクディスパッチを実現し、scheduler.yield()は長時間タスクを回避する精密なメインスレッド譲渡を提供し、TaskControllerはキャンセルと動的優先度調整をサポートします。これは高性能・高応答性Webアプリケーション構築の必須インフラです。

ブラウザローカルツールを無料で試す →

#Scheduler API#postTask#优先级#主线程#任务调度