2014-12-17

web workers三兄弟

Service workersの仕様を説明していて思ったのだが人々はどれぐらいweb workersのことについて詳しいのだろうか。

あんがい知らない気がするので簡単に解説してみる。まあ俺もニワカです。

共通部分

ブラウザのなかで、ウェブページとはある程度独立して、ある種の処理をするためのスクリプトを実行するモノのことをworkerと呼んでいる。ようするにJSの実行環境というかVMインスタンスというか。

ブラウザのなかでふつうのJSの実行環境であるタブの中とはいろいろ違うが、
  • windowオブジェクトがない。一部のJS APIは使えないしdocumentなどは持たない。DOMツリーもさわれない
  • ただしメッセージループは持つ。windowはないがselfというやつがいてこいつを使う(self.onmessage = function() { ... } みたいな感じ)
  • タブのなかとはmessage eventで通信する。postMessageというのを送ってmessageイベントをlistenする。JSオブジェクト(というかJSONというか)やArrayBufferは送れる
ええっと、こんだけか。

Workerは、ほかのプログラミング言語だとactorとか呼ばれるものに近い。実行環境は完全に分かれているので互いに隔離されている。共有データはないのでデータはコピーするしかない。所有権を譲渡する場合は参照だけ渡す最適化もあるけどそうなると手元からは読めなくなるし、まあそういう感じ。

ああっと補足をしておくと、Workerはみな起動時にスクリプトのURLを指定する。ふつうのプログラミング言語では、関数とかそういう単位を指定するとそいつが並行実行される気がするけれど、ファイルなのでそれより独立性が高く、グローバル変数も含めて環境は一切共有されない。共通の定数とかを使いたい場合、Workerには importScripts() という関数があってほかのJSファイルを読めるのでそういうのを使うことになる。

その場でスクリプトの生成をしたり特定の関数だけ呼びたい場合は、それを文字列化してBlobを作り、Blob URLを渡すというのが作法のようである。ただまあそれもWorkerならいいけどShared Workerではどうかなあ(Service workerは仕様上、Blob URLは使えない)。

Worker

簡単に言うとスレッドみたいなやつ。new Worker(scriptUrl)で作ると新しいworkerができる。元のスクリプトの実行環境とは隔離されていて非同期に動く。そんだけ。大元のタブが所有権を持っており、そいつが消えると自分も消える。

Web workerというと、このworkerのことを指すことが多い気がする(自分もそういうふうに使っていたこともある気がする)。一番の基本形。

Chrome / Firefox / Safari / Opera はかなり前からサポートしている。IEは10からっぽい。

Shared Worker

new SharedWorker(scriptUrl)でつくるやつ。Workerに似ているんだけど、同じURLに対してはひとつのインスタンスしか作られない。ふたつの違うタブから作ってもひとつのインスタンスで共有される。タブ間で通信したりデータ共有したりするのに使える。頑張ればタブ間でコネクションの一本化とかにも使えるだろう。

共有されているが、参照するやつがいなくなったら消滅する。タブを全部閉じたりとか。

タブ間通信とかチョー便利じゃんなんでみんな使わないの、Chromeとかバージョン4から使えるよ、と思ったらIEは未サポート、Safariは途中でサポートが消滅という悲しい目にあっている。可哀想な子……。

Service Worker

解説を書いた。navigator.serviceWorker.register(scriptUrl) で登録する。いっけんShared workerに似ているけれど、違いとしては、
  • 登録元のタブと独立した生存期間を持つ。タブが閉じてても生きてることがある。
  • フェッチなどオフライン処理に関係したイベントやAPIを持つ。したがってスクリプトの側からはオプショナルな機能として実現できる。
といったところ? それにほかの面白機能のベースになるもよう。

先進的すぎてChromeでもまだ安定版では使えない、Firefoxもフラグに隠されている、ほかのブラウザはやる気があるのかどうかすら不明ですが、面白いと思ってる。