目的
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の反応性を守りましょう。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
この記事をシェア







![Linuxをマスターしたい人のための実践Ubuntu[第2版]](https://m.media-amazon.com/images/I/413EBjNISEL._SL500_.jpg)
