icon長らくの間、JavaScriptはシングルスレッドな言語でした。重い処理を実行するたびにUI処理はブロックされ、マルチコアCPUは上手に使えない、それがJavaScriptの常識でした。Workersがあればそれが変わります。WorkersとはJavaScriptは、Workerという処理単位を持ちます。Workerはバックグラウンドで非同期に処理を行うための最も簡単な手法であり、JavaScriptに他の処理を妨げないタスクを実行する手段をもたらします。Workerという名前なのでしょうか。プログラマにとってなじみ深い「スレッド」ではいけなかったのでしょうか。事実、Workerはスレッドのようなものと言えます。Workerにはプログラマが一般的に想像するスレッドとは少し違う点があります。スレッドというよりかは、どちらかというと「プロセス」に近いかもしれません。Workerはそれぞれ異なる独立したメモリ空間を持ちます。簡単に言うと、複数のWorkerから同じ変数にアクセスすることはできないということです。Bの値にアクセスできませんし、逆も同じです。同じ値に同時にアクセスできない!まさか!では、いったいどうやって協調動作するというのでしょうか。メッセージング同じ変数にアクセスできないのは困ったことです。マルチスレッド(ここではマルチWorkerという方が正しいでしょうか)処理では、複数のスレッドが協調動作することによって高速な動作を実現することができるからです。Workerにはそのための仕組みがありません。かわりに、Workerにはメッセージングという仕組みがあります。名前の通り、互いにメッセージを送りあう仕組みです。ここでメッセージというのは、いわゆるオブジェクトのことです。オブジェクトを送りあうことで、またオブジェクトを送りあうことのみで、Workerは協調動作を実現します。常にスレッドセーフ独立したメモリ空間とメッセージング。Workerはなぜ一般的なスレッドのように振舞わず、このような回りくどい方法をとるのでしょうか。この面倒な方法には大きな利点があります。それは常にスレッドセーフだということです。そもそもお互いの変数にアクセスすることができないのですから、スレッドセーフかどうかを気にする必要がありません。Workerがこの仕組みを採用したことで、常に安全なマルチスレッドプログラムを書くことができるのです。ここから次のことも言えます:Workerはスレッドセーフではない機能は使えない。Storageがそれにあたります。(2017/10/31日追記)SharedArrayBufferWorker間で値を共有するための新しい仕組みとして、SharedArrayBufferが導入されました。APIを用いてWorker間でデータを共有する」をご覧ください。Workerの使い方Workerの作成Workerの作成方法は実に簡単です。Workerオブジェクトをnewするだけで作ることができます。Worker('worker.js');このとき、生成するWorkerの処理は別のファイル(ここではworker.js)に書いておく必要があります。メッセージの送信と受信Workerを生成したらメッセージを送信しましょう。メッセージの送信にはWorkerオブジェクトのpostMessageメソッドを使用します。送信可能なメッセージはJSONオブジェクトのみです。メッセージを送信するworker.postMessage('こんにちは!');しかしこれだけでは何も起こりません。Worker側の処理を書いていないから当然です。次はworker.jsファイルを編集してWorker側の処理を書きましょう。console.log(message.data);});Worker内でグローバルスコープを取得するには、selfを使います。メッセージの受信はmessageイベントで行います。ここでは受信したメッセージの内容をコンソールに出力するだけのコードを書いています。これを実行するとコンソールに「こんにちは!」と表示されます。メッセージの送受信がうまくいった証拠です。Worker側からもメッセージを送信することができます。Worker側からメッセージを送信するには、同じくpostMessageメソッドを使用します。self.postMessage(`こんにちは、${name}さん!`);});これを実行すると「こんにちは、古都ことさん!」と表示されます。エラー処理エラーを処理するにはerrorイベントを使用します。undefinedVariable;});Workerの終了Workerを終了させるにはterminateメソッドを呼び出します。worker.terminate();あるいはWorker側でcloseメソッドを呼び出します。self.close();TransferableオブジェクトWorkerのメッセージ送信は「コピー」です。感のいい方は気づいたかもしれませんが、大きなメッセージを送信すると、その分だけ時間がかかります。これは大きな問題です。巨大な処理をするためにWorkerを起動するのに、その巨大なオブジェクトのコピーを作るのに結局時間がかかってしまうというのは、本末転倒です。そこでJavaScriptではTransferableオブジェクトというものがあります。これは名前の通り、コピーではなく「転送」することができるオブジェクトです。Tranferableオブジェクトを利用することで、短い時間でメッセージを送信することができます。Transferableオブジェクトは、現在のところArrayBufferとMessagePortだけです。身近なArrayBufferはTypedArrayオブジェクトのbufferプロパティでしょう。[data.buffer]);ここでのポイントは、postMessageメソッドのふたつめの引数に、Transferableオブジェクトを配列で渡すことです。することで、値がコピーされずに転送されるようになり、メッセージ送信が高速で終わります。前述した通り、Workerにはいくつかの制限があります。Worker上において、次のオブジェクトはアクセス可能です:navigatorlocationXHRしかし次のオブジェクトにはアクセスできません:windowdocumentDOM制限に気をつけつつ、可能な範囲でWorkerを活用しましょう。重い処理の委譲Workerはメインスレッドをブロックしません。よって、重い処理を実行させても「スクリプトの応答がありません」などとダイアログが表示されることもありません。これを利用して重い処理をWorkerにやらせるという手法をとることができます。高速化せずに素朴にコードを書くとなかなか重い処理になります。これをWorkerにやらせてみましょう。メッセージを送信するworker.postMessage(2038074743);まずはWorkerに数値を送信します。Workerからbool値で判定を返してもらい、素数かどうかをコンソールに表示します。2038074743というのはなかなか大きな素数で、判定には時間がかかるはずです。次にworker.js側を見てみましょう。結果をメッセージとして送信します。self.postMessage(isPrime);});worker.js側では受け取った数値を素数かどうか判定し、判定をbool値で送信します。このとき、WorkerがUIスレッドをブロックしないことを確認するため、できるだけ時間がかかるようにしたいので、あえて高速化しない実装にしてあります。これを実行すると、しばらく時間が経ったのち、「素数です!」と表示されます。実行に時間はかかりますが、スクリプトの実行中止を促すダイアログは表示されません。Workerを活用することで、UIスレッドを妨げることなく重い処理を実行することができます。処理を高速化するWorkerの他の活用方法として、処理を高速化するというものがあります。Workerは複数生成することができるので、うまく使えば重い処理を高速化することができます。先ほどの素数判定を考えてみましょう。素数判定は2から順番に数値で割れるか確認していくだけなので、簡単に複数のジョブに分割することができます。それぞれのジョブをWorkerに割り振れば、マルチスレッドで高速な処理が実現できるはずです。メインファイルを書きましょう。程度がおおまかな高速化の限度。console.timeEnd('primeNumber');});メインファイルでの処理は簡単です。数字をジョブ数で分割して、各Workerに割り当てます。すべてのWorkerの処理が終了したら、結果を表示します。次にworker.jsを見てみましょう。こちらはほとんど変更点はありません。結果をメッセージとして送信します。self.postMessage(isPrime);});変更点はisPrimeNumber関数が処理範囲を引数として受け取るようになっただけです。これを実行してみましょう。計算結果と計算時間が表示されるはずです。MAX_WORKERSの値を色々変えて試してみましょう。1のときは12030ms、4のときは4541ms、8のときは3685msとなり、Worker数に応じた高速化が確認できます。Workersを使用することで、JavaScriptにおいてもマルチスレッド処理を実現することができました。いくつかの制限や使用方法のコツなどはありますが、シンプルで使いやすい作りになっています。メモリは各Workerで独立しており、協調動作はメッセージングによって行われます。Workerを使用することで裏で重い処理をすることができたり、並列処理による高速化が実現できます。適切に使用することができれば、心強い味方になるでしょう。Modules(import/export)ことはじめGamepadAPIでブラウザ上からゲームパッドの入力を取得するRSSプロフィール古都ことふるみやことです。フロントエンドとかいろいろやるよ。