Web Workerを利用した並列処理

2018.12.03

Javascriptでマルチスレッド処理

今回はService Workerを中心に、Web Workerを利用したJavaScriptの並列処理を紹介したいと思います。

Web Workerとは

Web Workerとはwebコンテンツにおいて、シングルスレッドを前提とした言語であるJavaScriptをマルチスレッドで実行するための仕組みです。
負担の大きい処理を、メインスレッドから切り離して実行するために導入されました。
Web Workerはnew Worker(xxx.js)と記述することで、バックグラウンドスレッドを新しく作成し、引数に設定したjsファイルの処理を行うことができます。
新しく作成されたスレッドは、ユーザインターフェイスを妨げることなくメインスレッドと並列でタスクを実行することができ、多量の計算など負荷が大きくてシングルスレッドでは厳しい処理を可能にすることができます。
さらに、生成されたWorkerオブジェクトにより、メインスレッド・バックグラウンドスレッド間でメッセージの送受信を行うことができ、メインスレッド・バックグラウンドスレッド間での密な連携が可能です。

ただ、Workerスレッドは、現在のwindowとは異なるグローバルコンテキストで実行されるため、Workerスレッド内で現在のグローバルスコープを取得するためにwindowを使用しても、エラーが返るなど、windowオブジェクトのデフォルトのメソッドやプロパティで、使用できないものがあるので注意が必要です。

対応ブラウザ

現在のWeb Workerの対応ブラウザは下記の通りです。

  • IE:10以上
  • Edge:12以上
  • Firefox:3.5以上
  • Safari:4以上
  • iOS Safari:5以上

主要ブラウザではほぼサポートされています。

様々なWorker

Workerは大きく三つに分けることができ、それぞれに特徴があります。

Dedicated Worker

前述したようなひとつのスクリプトが利用する標準的なWorkerです。

Shared Worker

Dedicated Workerとは異なり、生成元が同一であれば、複数のスクリプトからアクセスできるWorkerです。例えば、SharedWorkerを使用したサイトを開いていたとして、別タブで同じサイトを開くとき、メインスレッドでは既存のworkerスレッドを検索する処理が動きます。ページ間でWorkerを共有することで、表示ページ間でのメッセージ通信が可能になり、共通の処理を一回で済ますことができます。管理画面系のサービスやWebアプリケーションでは有用な機能と言えるでしょう。
しかし、IE・Edgeではサポートしておらず、使用する際は注意が必要です。

Service Worker

前述した二つのWorkerがページの内部で動く機能であるのに対して、Service Workerはページのすぐ外側で動く機能です。
「外側」というのは、Service Workerにページと外部の通信との間に割り込む機能があり、Web アプリケーション、ブラウザ、ネットワークの間 (可能であれば) でプロキシサーバーとして動作するよう設計されているためです。
そのため、Service Workerは高度なキャッシュの管理や、条件に応じた接続先の切り替えができるなど、触れてみると想像以上に自由度の高い機能です。
具体的には下記のことが可能だとされています。

  • ウェブからのプッシュ通知
    (GoogleクラウドメッセージングやFirebase Cloud Messagingからプッシュ通知を受け取り、ブラウザに表示させることが可能)
  • バックグラウンド同期
    (ブラウザを閉じていてもバックグラウンドでデータ受信したり、オフライン状態で受けた動作も通信可能になるとバックグラウンドで動作することが可能)
  • データのキャッシュ
    (指定したページや画像等をCache APIを使って保存、Fetchイベントハンドラで取り出すことが可能)
  • リクエストの横取り
    (ブラウザから投げられるリクエストを横取りして、サーバに投げる前に加工したり、
    サーバにリクエストを投げずに、固定のページをレスポンスとして返したりすることが可能)

ただ、Service Workerはhttpsかlocalhost上でしか動作しません。
また、こちらもIEは未対応なので、注意が必要です。

Service Workerを利用したプッシュ通知の実装

Service Workerを利用して下記のようなプッシュ通知を実装します。

環境について

下記環境で実装を進めます。

  • Node.js 8.11.4
    (web-pushモジュールを使用するので、あらかじめインストールしておいてください。)

ファイル構成

  • index.html
    (プッシュ通知に必要な情報を表示します。)
  • manifest.json
    (プッシュ通知送信情報を記述します。)
  • serviceworker.js
    (ServiceWorkerのイベントを設定します。)
  • main.js
    (ServiceWorkerの登録など、メインスレッドのイベントを設定します。)
  • push.js
    (プッシュ通知を送信するスクリプトを記述します。)

プッシュ通知実装準備

プッシュ通知を送信するためには、Google Developer Console か Firebase Console にプロジェクトを作成する必要があります。
今回はFirebaseでプロジェクトを作成します。
まず、Firebase Consoleにアクセスしてください。
次に、「新規プロジェクト作成」をクリックし、プロジェクト名、国/地域を設定して、プロジェクトを作成します。
プロジェクトの作成が完了したら、左上の歯車マークをクリックして、プロジェクトの設定を選択してください。クラウドメッセージングタブに通知送信に必要な送信者IDとサーバーキーが作成されているので調べます。

これで準備は完了です。

実装

manifest.jsonを作成し、先ほどFirebaseで調べた送信者IDをgcm_sender_idに指定します。
また、プッシュ通知に表示させたい画像を設定します。

[manifest.json]

manifest.jsonのstart_urlに指定したファイルでmanifest.jsonを読み込みます。

[index.html]

続いて、メインスレッドでServiceWorkerを登録し、ページ上にプッシュ通知に必要な情報を表示させるようにします。

[index.html]
[main.js]

続いて、ServiceWorkerのイベントを設定します。

[serviceworker.js]

プッシュ通知を送信するスクリプトを記述します。

[push.js]

プッシュ通知の実行

index.htmlにアクセスします。

Service Workerへの登録が成功し、送信に必要な情報が表示されました。
表示された、エンドポイント、乱数、公開鍵をpush.jsに入力します。

その後コマンドプロンプトを開き、「node push.js」を実行します。

プッシュ通知が成功しました。
通知をクリックすると、指定したurlのページが表示されます。

Service Workerを利用したPWAの実装

続いて、ServiceWorkerを使用して、下図のような仕組みでオフライン時でも表示できるページを実装します。

ファイル構成

  • index.html
  • app.js
    (ServiceWorkerの登録など、メインスレッドのイベントを設定)
  • serviceworker.js
    (ServiceWorkerのイベントを設定)

実装

まず、表示させるページを作ります。

[index.html]

続いて、app.jsでServiceWorkerに登録します。

[app.js]

ServiceWorkerをインストールしてファイルをキャッシュし、キャッシュを利用してコンテンツを表示できるようにします。

[serviceworker.js]

オフライン表示確認

オフラインで表示されるか確認します。
ChromeのデバッグコンソールのApplicationタブのServiceWorkerの項目をクリックし、Offlineにチェックを入れると、オフライン環境下でのデバッグが可能です。

オフラインでもページを表示させることができました。

Web Workerの懸念点

ここまで、Service Workerを中心にWeb Workerについて説明してきました。
Web Workerは魅力的な機能ばかりですが、攻撃者が用意したスクリプトがWorkerとして実行されたり、importScripts()で攻撃者が用意したスクリプトが読み込まれたりするなどの懸念点もあります。
WorkerスレッドではDOM操作ができないのでそこまで大きな脅威にはならないと言えますが、対策として、外部からのコードがWorkerとして実行されないように注意することが必要でしょう。

まとめ

今回はService Workerを中心にWeb Workerについて説明しましたが、Dedicated Worker・Shared Workerも重い処理の実行や管理画面上での同期処理など、今後のブラウザサポート状況と応用次第では、うまく活用できると思いました。
最近ではWeb Workerに取って代わってWorkletという概念も登場してきているので、引き続き注目していきたいと思います。