代表的な面接トピック

フロントエンド面接:Web Worker、SharedWorker、Service Workerをどのように使い分けるか?

フロントエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

同一オリジンのアナリティクスアプリにおいて、200 MiBのCSVをローカルでパースし、タブ間で1つのWebSocketを共有し、オフライン時に最新レポートを開いて保存処理を再試行する必要があります。各要件に対してどのWorkerを選択し、ライフサイクル、通信、フォールバックをどのように処理しますか?

プロンプトと適用シナリオ

同一オリジンのアナリティクスアプリに、以下の3つの独立した要件があります:

  1. 入力やレンダリングの応答性を維持したまま、ユーザーが選択した200 MiBのCSVをローカルでパースする。
  2. 少なくとも1つのタブが開いている間、タブ間で1つのWebSocketとインメモリの購読状態を共有する。
  3. オフラインでアプリシェルと直近の正常なレポートを開き、保存処理を1件キューに登録して、接続復旧後に再試行する。

各要件について、Dedicated Worker、SharedWorker、Service Workerの中から選択してください。所有権(ownership)、ライフタイム、通信、永続化、キャンセルおよび障害処理、ブラウザのフォールバック、セキュリティ境界、バリデーションについて説明してください。

200 MiBのファイル、単一のWebSocket、オフライン保存は面接用の前提条件であり、普遍的なプロダクト目標ではありません。この質問はシニアフロントエンド、Webプラットフォーム、フロントエンドアーキテクチャの職種に適しています。コアとなるスキルはブラウザ実行とWebプラットフォームの選定であるため、カテゴリはfrontendです。

面接官が評価するポイント

第一に、候補者がすべてのバックグラウンドスレッドを一括りに「Web Worker」と呼ぶのではなく、誰が処理を所有し、どのくらいの期間生存する必要があり、どのページに影響を与えるかに基づいて選択できるか。優れた回答は、ページが所有する計算処理をDedicated Workerに、同一オリジンの複数ページにまたがる一時的な状態をSharedWorkerに、スコープ設定されたネットワークプロキシ、キャッシュ、イベント駆動のバックグラウンド処理をService Workerに割り当てます。

第二に、候補者がスレッド処理と永続化を分離して考えられるか。メインスレッドからコードを移動させても、CPU、メモリ、I/Oが自動的に削減されるわけではありません。SharedWorkerのメモリは永続的な状態ではありません。ブラウザはイベントの合間にService Workerを停止することがあるため、保留中の処理はIndexedDBなどの永続ストレージに配置する必要があります。

第三に、候補者がメッセージングのコストを定量化できるか。構造化複製(structured clone)は通常データをコピーします。ArrayBufferは転送可能であるため、バイト単位の複製を行わずに所有権を移動できますが、送信元のバッファは切り離されます(detached)。また、大容量CSVの処理にはチャンク分割、進捗通知、バックプレッシャー、キャンセル処理も必要です。

第四に、候補者が適切なフォールバックを提供できるか。SharedWorkerは現在のブラウザリリースでBaseline 2026に達しましたが、古いデバイスや一部の実装ではサポートされていない場合があります。Background Syncは依然としてBaselineではありません。正しさを1つのオプションAPIに依存させることはできません。

第五に、候補者がライフサイクルの障害をテストできるか。正常系の単一の操作フローだけでは不十分です。リロード、最後のタブを閉じる動作、Service Workerの更新、オフラインでの再起動、重複する同期イベント、キャッシュの汚染、古いブラウザ向けのフォールバックのすべてが重要です。

最初に明確にすべき質問

  • CSVのパースは単発ですか、それとも継続的ですか? 単発の処理であれば、オンデマンドでDedicated Workerを1つ作成できます。継続的な並行処理には、ファイルごとに新しいスレッドを作成するのではなく、制限付きのワーカプールとキューが必要です。
  • 入力全体をメモリ上に保持する必要がありますか? パーサーがサポートしている場合は、ストリーミングまたはチャンク分割された入力を優先します。ArrayBuffer全体を一度に移動させる必要がある場合は、未加工のバイト列、デコードされた文字列、パースされた値、インデックスに対して個別にメモリを見積もります。
  • すべてのタブは完全に同一オリジンですか? SharedWorkerには同一のスキーム、ホスト、ポートが必要です。サブドメイン、サードパーティiframe、異なる開発用ポートを使用すると、通信設計が変わります。
  • 1つのWebSocketは最適化ですか、それとも正しさのための制約ですか? 接続数の削減のみが目的であれば、タブごとの接続が有効なフォールバックになります。サーバーが単一セッションのみを許可している場合、フォールバックにはBroadcastChannel、リーダー選出、リース(lease)、スプリットブレインの回復処理が必要になります。
  • オフライン保存は自動的に再試行できますか? 機密性の高い操作や競合する操作では、ページが復帰した後にユーザーの確認が必要になる場合があります。自動再試行には、冪等性キー、永続的な状態、制限付きバックオフが必要です。
  • どの古いブラウザやプライバシーモードが対象ですか? その回答によって、SharedWorker、Background Sync、ストレージ容量制限、Service Workerの挙動に関する機能検出とフォールバックの境界が決まります。

30秒の回答フレームワーク

「私は所有権とライフタイムに基づいて要件を分割します。200 MiBのCSVは現在のページが所有するCPU負荷の高い処理であるため、Dedicated Workerを使用し、チャンク単位でパースし、進捗を報告し、所有権を移動できる場合はArrayBufferを転送し、協調的にキャンセルするかワーカーを強制終了します。同一オリジンのタブ間で共有する1つのWebSocketはSharedWorkerに配置し、各ページがMessagePortを介して購読できるようにします。機能検出を行い、BroadcastChannelとリーダー選出、または許容されるならタブごとの接続へのフォールバックを用意します。オフラインシェル、レポートのキャッシュ、後からの再試行には、スコープ内のリクエストをインターセプトしてバックグラウンドイベントを処理できるService Workerを使用します。キューは冪等性キーとともにIndexedDBに保存します。Background Syncがない環境では、ページの起動時、フォアグラウンド復帰時、再接続時にフラッシュします。最後のタブのクローズ、オフライン再起動、重複同期、Service Workerの更新をテストします。」

ステップ別の詳細解説

ステップ1:所有権、スコープ、ライフタイムに基づいて選択する

要件選択所有者と通信主な障害境界
現在のページのために大容量CSVをパースするDedicated Worker作成元のページ、postMessageページのクローズ、キャンセル、メモリピーク
同一オリジンのタブ間で接続を共有するSharedWorker同一オリジンのページ群、ページごとに1つのMessagePort最後の参照のクローズ、古いブラウザでの未サポート
オフラインシェル、ネットワークプロキシ、後からの同期Service Worker登録されたオリジンとパススコープ、イベントおよびクライアントメッセージ更新の待機、イベント間の終了、オプションAPIの未提供

3つのWorkerのいずれもDOMを直接操作することはできません。Dedicated WorkerとSharedWorkerは、ページに必要なJavaScriptの処理を実行します。Service Workerは、オリジンとパスに対して登録されるイベント駆動型のネットワークプロキシです。サポートされているブラウザではfetch、キャッシュ、Push、バックグラウンド同期を処理できますが、数分間連続して実行する必要があるCSVパースの置き場所としては不適切です。

ステップ2:200 MiBのCSVパースをDedicated Workerに分離する

タスクは現在のページのみに提供されるため、Dedicated Workerをオンデマンドで作成します。メインスレッドはファイル選択、進捗UI、結果のレンダリングを所有します。ワーカーはデコード、トークン化、型推論、集計を所有します。ワーカーはDOMアクセスを持たないため、メッセージを通じて結果を返します。

このインターフェースのスケッチでは、パーサーとエラーの型を省略しています。chunkBytesはワーカーに対して内部で4 MiBのバッチで進めるよう指示するものであり、メインスレッドが別のコピーを作成することを意味するわけではありません:

js
const parser = new Worker(
  new URL("./csv-parser.worker.js", import.meta.url),
  { type: "module" },
);

const jobId = crypto.randomUUID();
const buffer = await file.arrayBuffer();

parser.postMessage(
  { type: "parse", jobId, buffer, chunkBytes: 4 * 1024 * 1024 },
  [buffer],
);

parser.onmessage = ({ data }) => {
  if (data.jobId !== jobId) return;
  if (data.type === "progress") renderProgress(data.rows);
  if (data.type === "done") renderReport(data.summary);
};

cancelButton.onclick = () => {
  parser.postMessage({ type: "cancel", jobId });
};

キャンセルは協調的または強制的に行うことができます。パースループはチャンク境界でキャンセルフラグを確認し、一時オブジェクトを解放して最終状態を報告します。ワーカーがハングした場合やパーサーが処理を譲らない場合、terminate()で停止できますが、そのワーカー内のすべてのジョブが失われます。したがって、長時間実行される複数ファイル処理では、単一の区別のない共有ワーカーではなく、ジョブごとの分離が必要です。

ステップ3:コピー処理とピークメモリを考慮した設計

通常のpostMessageは構造化複製を使用します。送信側と受信側の両方に200 MiBのArrayBufferが存在するという単純化した前提では、未加工のバイト列だけで約400 MiBを消費します。これには、Fileのバッキングストア、デコードされた文字列、行オブジェクト、インデックス、ガベージコレクションのヘッドルームは含まれません。これは面接での概算であり、ブラウザのメモリ保証ではありません。

転送リスト(transfer list)にArrayBufferを配置すると、そのメモリリソースがワーカーに移動し、送信側のバッファが切り離されるため、バイト単位のコピーが回避されます。ページ側で元のバッファがまだ必要であり、所有権の設計ができていない場合は転送しないでください。より安全な大容量入力の処理フローは、Blob.stream()またはスライスを読み取り、小さなチャンクを送信し、増分統計を受信することです。メインスレッドは処理中のチャンク数を制限し、確認応答を受け取った後にのみ次のチャンクを送信することで、バックプレッシャーを生成します。

共有メモリはデフォルトの近道ではありません。SharedArrayBufferはクロスオリジン分離を必要とし、アトミックス、データ競合、より厳格なデプロイセキュリティを導入することになります。通常のメッセージングと転送可能オブジェクトがボトルネックであることが計測によって示される前に、その複雑さを追加すべきではありません。

ステップ4:同一オリジンの複数タブ接続をSharedWorkerに保持する

SharedWorkerは「同一オリジンのページが少なくとも1つ接続されている間、単一の共有インスタンスを維持する」という要件に合致します。各タブは同じURLで同じ名前のワーカーを開き、自身のMessagePortを介して購読を登録します。ワーカーはWebSocket、ポートのセット、インメモリの購読マップを所有し、サーバーからのメッセージを該当するポートにルーティングします。

js
const socketHub = new SharedWorker(
  new URL("./socket-hub.shared-worker.js", import.meta.url),
  { type: "module", name: "analytics-socket-hub" },
);

socketHub.port.start();
socketHub.port.postMessage({ type: "subscribe", reportId });
socketHub.port.onmessage = ({ data }) => applyLiveUpdate(data);

window.addEventListener("pagehide", () => {
  socketHub.port.postMessage({ type: "disconnect", reportId });
  socketHub.port.close();
});

完全に同一のオリジンのコンテキストのみがSharedWorkerにアクセスできます。開いているページが参照を保持している間は生存し続けますが、最後の参照が閉じられた後は、永続的なバックグラウンドサービスとしては機能しません。接続の切断、ブラウザの終了、ワーカーのクラッシュによって購読マップが消去される可能性があります。維持する必要があるサーバーカーソルを永続化するか、各ページに購読を再宣言させてください。

ステップ5:SharedWorkerに実用的なフォールバックを用意する

SharedWorkerで機能検出を行います。これが利用できない場合、タブ間でBroadcastChannelを介してハートビートとイベントを交換し、Web Locksや有効期限付きのストレージリースを使用して、WebSocketを所有する1つのリーダーを選出できます。リーダーが閉じた後、他のタブが後任を選出します。サーバーはセッションとイベントIDで重複を排除し、クライアントは単調増加するエポックを使用して古いリーダーからのメッセージを拒否し、スプリットブレインによる被害を抑制します。

そのフォールバックはSharedWorkerよりも大幅に複雑です。1つの接続への集約が単なるコスト最適化に過ぎない場合、最もシンプルで正しいフォールバックは、サーバー側の冪等性と接続制限を備えたタブごとのWebSocket接続です。選出、リースの更新、障害検出、フォールトインジェクションの構築は、厳格なサーバー側の制約によって単一接続が要求される場合にのみ行ってください。

ステップ6:オフライン動作と再試行にService Workerを使用する

Service Workerはオリジンとパススコープに対して登録され、制御下のページからのリクエストをインターセプトできます。アプリシェルにはバージョニングされた事前キャッシュ(precaching)またはCache-Firstが適しています。最新のレポートには多くの場合Network-Firstが適しています。成功したネットワークレスポンスでCache Storageを更新し、失敗時には最後に成功したキャッシュレスポンスを返します。ユーザーの保存キューは、Cache StorageやService Workerのグローバル変数ではなく、IndexedDBに配置します。

これもインターフェースのスケッチです。本番コードでは、キャッシュ可能なURLのホワイトリスト化、レスポンスの検証、キャッシュのバージョニングを行い、「キューに登録済み」と表示する前にIndexedDBに保存内容を書き込む必要があります:

js
navigator.serviceWorker.register("/sw.js", { scope: "/" });

// sw.js
self.addEventListener("install", (event) => {
  event.waitUntil(cacheAppShell());
});

self.addEventListener("fetch", (event) => {
  const url = new URL(event.request.url);
  if (url.pathname.startsWith("/reports/")) {
    event.respondWith(networkFirstReport(event.request));
  }
});

self.addEventListener("sync", (event) => {
  if (event.tag === "retry-report-saves") {
    event.waitUntil(flushIndexedDbQueue());
  }
});

Background Syncは一部のブラウザでのみ利用可能です。登録が失敗した場合やAPIが存在しない場合は、ページの起動時、フォアグラウンド復帰時、またはonlineイベントの受信時に同じキューフラッシュ処理を呼び出します。接続イベントは再試行のトリガーであり、成功の証明ではありません。サーバーのレスポンスのみが完了を確認します。各保存処理には、冪等性キー、試行回数、次回の再試行時刻を保存します。競合が発生した場合は、無制限に上書きするのではなく、確認待ちの状態に移行させます。

ステップ7:Service Workerのライフタイムと更新を処理する

Service Workerは、ダウンロード、インストール、待機、アクティベーションを経て初めてページを制御します。初回登録後、現在のドキュメントが制御下に入るには通常、別のナビゲーションが必要です。新しいバージョンがインストールされ、古いバージョンを使用しているページが閉じるのを待機する場合があります。skipWaiting()clients.claim()を使用すると制御を迅速に引き継ぐことができますが、プロトコルが異なる場合、古いページと新しいワーカーの組み合わせによって問題が発生する可能性があります。メッセージおよびキャッシュのスキーマは適切にバージョニングしてください。

ブラウザはイベントの合間にワーカーを終了させ、次のイベントで再起動することがあります。event.waitUntil()は現在のイベントのPromiseに関連付けられたライフタイムを延長するものであり、常駐プロセスを作成するわけではありません。キュー、試行回数、冪等性キーを永続化してください。同期処理は任意のレコードから再開でき、同じリクエストを安全に2回実行できるように設計する必要があります。

Service Workerにはセキュアコンテキストが必要です。本番環境ではHTTPSが必須であり、localhostは開発用の例外です。デフォルトのスコープはスクリプトのパスによって制限されます。また、CSPのworker-src、ユーザー機密のキャッシュ内容、ログアウト時の分離、クロスオリジンリクエストに対するCORSとクレデンシャルの挙動も検証してください。

ステップ8:障害マトリクスによる検証

Dedicated Workerを1 MiBおよび200 MiBのファイル、不正な形式の入力、キャンセル操作でテストします。メインスレッドのLong Task、入力遅延、パーススループット、ピークメモリ、キャンセル後の解放時間を記録します。構造化複製、転送可能オブジェクト、チャンク分割の各バリアントを比較し、計測されたボトルネックに基づいて選択します。

SharedWorkerを2つの同時購読、リーダーページのクローズ、最後のページのクローズ、ネットワークの断続的な切断、ワーカーエラー、SharedWorkerのないブラウザでテストします。受け入れ確認には、実際のサーバー接続数、予期しないメッセージの欠落や重複がないこと、再接続後のカーソルの継続性、正しさを維持するフォールバックが含まれます。

Service Workerを初回訪問、制御された再訪問、オフラインでのリロード、キャッシュの有効期限切れ、待機中の更新、ブラウザの強制終了、重複したsync、ストレージ容量の枯渇、Background Syncの未提供環境でテストします。保存エンドポイントで冪等性キーを使用して、少なくとも1回(at-least-once)の試行によって重複書き込みが発生しないことを証明します。キャッシュされたプライベートデータがユーザー間で混ざらないことを検証します。

高品質な回答例

「タスクを誰が所有しているか、どのくらい生存する必要があるか、ネットワークをプロキシするかどうかから考えます。200 MiBのCSVは現在のページのみに提供され、CPU負荷が高いため、Dedicated Workerを使用します。メインスレッドはファイル選択とUIを処理し、ワーカーはチャンクをパースし、進捗を報告し、チャンク間でキャンセルを確認します。通常のメッセージングには構造化複製が使用されます。両側に完全な200 MiBのバッファが存在する場合、面接の前提では未加工のバイト列だけで400 MiB近くになるため、所有権を移動する転送可能なArrayBufferや、処理中数を制限したチャンクのストリーミングをベンチマークします。

タブ間で共有する1つのWebSocketはSharedWorkerに保持します。すべてのページは完全に同一オリジンである必要があり、それぞれのMessagePortを介して購読を再宣言します。ワーカーには再構築可能な接続およびルーティング状態のみを保持します。これは永続化ではないため、最後のページが閉じたとき、ブラウザが終了したとき、またはワーカーがクラッシュしたときに状態が消失する可能性があります。これについては機能検出を行います。単一接続が単なる最適化である場合、フォールバックはタブごとに1つの接続です。厳格な制約である場合は、スプリットブレインと重複に対応するためにエポックとイベントIDを備えたBroadcastChannelとリースベースの選出を使用します。

オフラインシェル、最新レポート、後からの保存にはService Workerを使用します。スコープ内のリクエストをインターセプトし、シェルにはバージョニングされた事前キャッシュ、レポートにはキャッシュフォールバック付きのNetwork-Firstを適用します。保存データは、バックグラウンド同期を登録する前に冪等性キーとともにIndexedDBに書き込まれます。Background Syncがない場合、起動、フォアグラウンド復帰、または再接続によって同じフラッシュ処理が呼び出されます。Service Workerはイベント間で停止する可能性があるため、キューがグローバル変数のみに存在することはありません。実行ごとに永続化された状態を復元し、現在の処理をwaitUntil()に関連付けます。

最後に、メインスレッドのLong Taskとメモリ、実際のWebSocket接続数と再接続の挙動、オフライン再起動と重複同期、さらにService Workerの更新待機、古いブラウザのフォールバック、ユーザーごとのキャッシュ分離、HTTPSとCSPの境界を検証します。」

よくある間違い

  • Service Workerを長時間実行される計算スレッドとして使用する → ブラウザがイベント間や長時間の処理中に終了させる可能性があります → ページの計算にはDedicated Workerを使用し、Service Workerはイベント駆動のネットワーク処理専用にします。
  • メモリを計測せずにpostMessageで200 MiBのオブジェクトを送信する → 構造化複製とパース結果が同時に存在し、大きなピークが発生する可能性があります → 転送可能、チャンク分割、ストリーミングの各パスを比較し、ピークを計測してください。
  • ワーカーを使えばアプリの応答性が保証されると思い込む → CPU、ガベージコレクション、シリアライズには依然として時間がかかります → メインスレッドのLong Task、スループット、メッセージのバッチサイズを計測してください。
  • SharedWorkerのメモリを信頼できる状態として扱う → 最後の参照が閉じられたり、ブラウザが終了したりすると消失する可能性があります → 再構築可能なランタイム状態のみを保持し、重要なカーソルやキューは永続化してください。
  • 厳密な同一オリジン要件を無視する → スキーム、ホスト、ポートが異なる場合は共有できません → クライアント側の協調動作を選択する前に、オリジンの境界をマッピングしてください。
  • SharedWorkerが利用できないときに常に複雑な選出システムを複製する → ビジネス上必要なのは厳密に1つの接続ではなく、正しさである場合があります → 接続数が厳格な制約であるかどうかを判断し、そうでない場合はタブごとの接続にフォールバックしてください。
  • オフラインキューをService Workerのグローバル変数に保持する → ワーカーの再起動によってジョブが失われます → 最初にIndexedDBに書き込み、リプレイセーフなフラッシュ処理を呼び出してください。
  • Background Syncのみに依存する → すべての主要ブラウザでサポートされているBaseline機能ではありません → 起動時、フォアグラウンド復帰時、再接続時にも再試行してください。
  • すべての更新に即座に制御を引き継がせる → 古いページと新しいワーカーの間でプロトコルに互換性がない可能性があります → 待機のスキップを選択する前に、メッセージ、キャッシュ、マイグレーションをバージョニングしてください。
  • すべての正常なレスポンスをキャッシュする → プライベートデータがアカウント間で残ったり、誤って再利用されたりする可能性があります → URLホワイトリスト、ユーザーごとの分離、ログアウト時のクリーンアップ、レスポンスの検証を使用してください。

フォローアップの質問と回答

フォローアップ1:CSVが2 GiBに肥大化し、モバイルのメモリが不足する場合は何を変更しますか?

入力全体に対してfile.arrayBuffer()を呼び出すのをやめます。Blob.stream()またはslice()のチャンクを読み取り、デコード境界をまたいで不完全な行を保持し、ワーカーが集計値やカラムナバッチを返すようにします。処理中のチャンク数を制限し、バックプレッシャーのための確認応答を必須とします。キャンセルのポイントを公開します。プロダクトがすべての行を保持する必要がある場合は、JavaScriptヒープ上に完全なオブジェクトグラフを保持する代わりに、IndexedDBまたは分割ファイルに退避(spill)させ、結果をページングします。デバイス固有の制限と明示的な拒否パスを追加します。

フォローアップ2:異なるサブドメインのページで厳密に1つのWebSocketを共有する必要がある場合はどうしますか?

SharedWorkerは厳密なオリジン境界を越えることができません。1つの選択肢は、アクティブな接続を所有する制御されたオリジンを用意し、targetOrigin、送信者、スキーマの厳格な検証を備えた監査済みiframeメッセージブリッジを公開することです。もう1つの選択肢は、各ページが独立したクライアント接続を維持しながら、単一接続の協調動作をサーバー側に移動することです。前者はセキュリティと可用性が密結合し、後者はサーバー接続のコストが増加します。同一オリジンポリシーを回避しようとするのではなく、実際の制約に基づいて選択してください。

フォローアップ3:SharedWorkerはBaseline 2026ですが、なぜフォールバックを維持するのですか?

プロダクトのブラウザマトリクスに基づいて判断します。Baseline 2026は現在のブラウザリリースで新たに普及した機能を示すものであり、すべての古いデバイス、固定されたエンタープライズバージョン、プライバシーモード、組み込み環境を網羅しているわけではありません。機能検出のコストは低いです。フォールバックとしてはタブごとの接続から始め、接続に関する厳格な制約によって正当化される場合にのみ選出ロジックを構築します。

フォローアップ4:Background Syncが保存処理を繰り返す際、重複書き込みをどのように防ぎますか?

クライアントはビジネス操作に対して1つの安定した冪等性キーを生成します。キューには少なくともそのペイロード、キー、試行回数、次回の再試行時刻を保存します。サーバーは一意性を強制するか、ユーザーとキーごとに結果を保存し、重複に対しては元の結果を返します。クライアントは確認を受け取った後にのみキューアイテムを削除します。タイムアウトは結果が不明であることを意味し、新しいキーではなく同じキーで再試行します。ビジネスバージョンの競合は、暗黙的な上書きではなく確認待ちの状態に移行させます。

フォローアップ5:新しいService Workerがインストールされましたが、ユーザーが古いページを無期限に開き続けています。どう対応しますか?

制御下のページに更新の準備ができたことを通知し、安全なタイミングでユーザーにリロードを促します。セキュリティ修正においてskipWaiting()clients.claim()を使用できるのは、古いページと新しいページがワーカーのメッセージプロトコルと互換性を維持しており、キャッシュやIndexedDBのマイグレーションが再入可能(reentrant)である場合のみです。リリース前のテストでは、古いページと新しいワーカーの組み合わせを検証する必要があります。互換性が保証できない場合は、即時アクティベーションのみを優先するのではなく、待機フェーズを維持します。

フォローアップ6:タブ間でのWebSocket共有にもService Workerを使用しないのはなぜですか?

Service Workerのライフタイムはイベント駆動型であり、ブラウザはアイドル時に停止させることができます。WebSocketには継続的な接続とインメモリセッションが必要であり、これはそのモデルと矛盾します。ページの参照が残っている間は、SharedWorkerの方が適切な所有者です。対象のブラウザにSharedWorkerがなく、単一接続が必須である場合は、Service Workerが常駐することを前提にするのではなく、タブ間の選出を使用するかサーバーアーキテクチャを変更してください。

公開情報ソース

関連する質問