Tasuke HubLearn · Solve · Grow
#Web Performance

Web Worker設計パターン集【2025年版】:UIをブロックしない並行処理の実践

重い計算やパース、画像処理、検索、圧縮などをUIスレッドから切り離すためのWeb Worker設計パターン。メッセージング、キャンセル、プーリング、型安全、共有キャッシュ、Comlink連携、Next.js統合まで。

時計のアイコン13 September, 2025
TH

Tasuke Hub管理人

東証プライム市場上場企業エンジニア

情報系修士卒業後、大手IT企業にてフルスタックエンジニアとして活躍。 Webアプリケーション開発からクラウドインフラ構築まで幅広い技術に精通し、 複数のプロジェクトでリードエンジニアを担当。 技術ブログやオープンソースへの貢献を通じて、日本のIT技術コミュニティに積極的に関わっている。

🎓情報系修士🏢東証プライム上場企業💻フルスタックエンジニア📝技術ブログ執筆者

目的

INP を悪化させる重処理を UI スレッドから分離するための設計カタログ。実装スニペット付きですぐ使えます。


ベストマッチ

最短で課題解決する一冊

この記事の内容と高い親和性が確認できたベストマッチです。早めにチェックしておきましょう。

基本:型安全なメッセージング

// types.ts
export type Req = { id: string; kind: 'parse' | 'search' | 'cancel'; payload?: unknown };
export type Res = { id: string; ok: true; data: unknown } | { id: string; ok: false; error: string };
// worker.ts
self.onmessage = (e: MessageEvent<Req>) => {
  const { id, kind, payload } = e.data;
  try {
    if (kind === 'cancel') return cancelJob(id);
    const data = kind === 'parse' ? parse(payload) : search(payload);
    (self as any).postMessage({ id, ok: true, data } as Res);
  } catch (err: any) {
    (self as any).postMessage({ id, ok: false, error: String(err) } as Res);
  }
};

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

パターン1:キャンセル可能なジョブ

// main.ts
const worker = new Worker(new URL('./worker.ts', import.meta.url));
const controller = new AbortController();
worker.postMessage({ id: 'job-1', kind: 'search', payload: { q: 'foo' } });
controller.signal.addEventListener('abort', () => worker.postMessage({ id: 'job-1', kind: 'cancel' }));

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

パターン2:プーリング(並列ワーカー)

class WorkerPool {
  private q: any[] = [];
  private busy = new Set<Worker>();
  private pool: Worker[];
  constructor(size = Math.max(2, (navigator as any).hardwareConcurrency ?? 4)) {
    this.pool = Array.from({ length: size }, () => new Worker(new URL('./worker.ts', import.meta.url)));
  }
  exec(task: any) {
    const idle = this.pool.find(w => !this.busy.has(w));
    if (!idle) return this.q.push(task);
    this.busy.add(idle);
    idle.onmessage = () => { this.busy.delete(idle); const next = this.q.shift(); next && this.exec(next); };
    idle.postMessage(task);
  }
}

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

パターン3:SharedWorkerで横断キャッシュ

// shared-worker.ts
const cache = new Map<string, any>();
onconnect = (e: any) => {
  const port = e.ports[0];
  port.onmessage = (ev: any) => {
    const { key, compute } = ev.data;
    if (!cache.has(key)) cache.set(key, heavy(compute));
    port.postMessage(cache.get(key));
  };
};

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

パターン4:ComlinkでRPC風に呼び出す

import * as Comlink from 'comlink';
export type API = { hash(data: Uint8Array): Promise<string> };
const api: API = { async hash(data) { return await sha256(data); } };
Comlink.expose(api);

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

パターン5:ストリーミング処理(逐次パース)

// 逐次的にpostMessageしてUIに小出しに反映
for await (const chunk of stream) {
  (self as any).postMessage({ type: 'progress', chunk });
}

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

Next.js への統合のポイント

  • バンドラ設定(new URL('./worker.ts', import.meta.url) で静的解析)
  • Route Handlers と連携し、前処理をワーカーに委譲
  • 型共有(types.ts)で安全なメッセージング

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

運用チェックリスト

  • メインスレッドで長いタスクが発生していない
  • キャンセル・タイムアウト・再試行が設計されている
  • メモリ使用量とワーカー寿命を監視している
  • INPに改善効果が出ている

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

まとめ

ワーカーは“重処理の避難所”。メッセージ設計とキャンセル、プーリング、キャッシュを組み合わせ、UIの反応性を守りましょう。

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

この記事をシェア

続けて読みたい記事

編集部がピックアップした関連記事で学びを広げましょう。

#LLM

LLM推論コスト最適化パターン集【2025年版】:品質を落とさず費用を半減する

2025/9/13
#Web Performance

フロントエンド性能最適化チェックリスト【2025年版】:Core Web Vitalsで確実に速くする

2025/9/13
#カスタマーサポート

【2025年版】マルチモーダル顧客サポートの実践ガイド

2025/11/23
#データ

【2025年版】シンセティックデータガバナンス実践ガイド

2025/11/23
#AI

【2025年版】エンタープライズAIガードレール設計ガイド

2025/11/23
#Web Performance

INP最適化決定版【2025年版】:反応性を根本から改善する実践テクニック

2025/9/13