開発室ブログ

JavaScript Web API

JavaScriptの重い処理にWeb Workerを使う

こんにちは。
僕のブログ記事を見返すと、ここ2回ほど某不動産屋が爆発した話とか、改元の話について書いていて技術ネタを書いていない事に気がついたので今回は技術ネタで…。

JavaScript好きですか?

みなさんJavaScript好きですか?好きですよね。

僕は好きです。

僕は元々PHPerなのですが(というより気がついたらPHPが一番長く使っている言語になってた)
ES2015(ES6)が出てきて(しばらくして)からJavaScriptが好きになってきました。
(これはES2015のおかげというよりはReactとかVueとかAngularの台頭が理由かも…)

ES2015については今回の本題ではないので詳しくは説明しませんが、ES2015以降JavaScriptに様々な構文が追加され書きやすく見やすくなりました。
代表的なものでいうとletやconst、アロー関数やクラス構文などなど。
更にES2016~ES2018もあり、次々と便利な構文などが追加され続けています。
ES2015対応ブラウザ
モダンブラウザであればES2015ほぼ全て対応していますので使っていきましょう。

ちなみに、余談ですが、JavaScriptの正式な書き方はJavaScriptです。
javascriptでもJavascriptでもJavaでもありません。

もし口頭で略すならジェイエス(JS)ですかね。(個人的には)

JavaScriptはシングルスレッド

JavaScriptはシングルスレッドです。

シングルスレッドって何かというと順次処理、とか単一処理とか言われたりしますが、要は記述された順番に処理を行う(前の処理が終わってから次の処理を行う)処理方式です。
なので、途中に単一で重い処理が入ってくると、その処理中他の処理は実行されません。

非同期処理で一部の処理を後回しにするなど工夫できるケースもありますが、今回のように単一で重い処理の場合は非同期処理は役に立ちません。

非同期処理とは並列処理のことではない

非同期処理の事を並列処理と勘違いしていそうな方もいる気がするのですが、非同期処理は並列処理ではなく実行を順不同で行うようにする平行処理にあたります。

そのため、非同期処理を使って一部処理を後回しにするなど、処理を分散させることで負荷を軽減することはできますが、単体で重い処理の場合は後回しにしたところでブラウザがフリーズしてしまうなどの問題が発生してしまう可能性があります。

じゃあどうするのか

Web Worker。Web Workerを使う。

Web Worker

Web Worker

詳しくは上記ページを見ていただければと思います。

Web Worker とは、ウェブアプリケーションにおけるスクリプトの処理をメインとは別のスレッドに移し、バックグラウンドでの実行を可能にする仕組みのことです。時間のかかる処理を別のスレッドに移すことが出来るため、 UI を担当するメインスレッドの処理を中断・遅延させずに実行できるという利点があります。

そのままです。

実際に使ってみる

index.js

const worker = new Worker('worker.js');
const text = 'よいこのみんなー!';
worker.postMessage(text);
worker.addEventListener('message', (response) => {
  console.log(response);
});

worker.js

self.addEventListener('message', (text) => {
  const response = text + 'こんにっちはー!';
  self.postMessage(response);
});

これだけでOKです。

Workerオブジェクトを作成する

const worker = new Worker('worker.js');

Workerクラスのコンストラクタに、使用するjsファイルパスを指定してWorkerオブジェクトを作成します。

Worker.postMessageでWorkerにpostする

const text = 'よいこのみんなー!';
worker.postMessage(text);

先程作成したWorkerオブジェクト(worker)のpostMessageに任意の変数を渡します。
postMessageではほぼどんなデータでも送信することができます。

postされたメッセージをWorkerで受け取る

self.addEventListener('message', callback);

index.jsからpostMessageでメッセージを受け取った際にはonmessageハンドラで、何らかの処理を行うことが可能です。
addEventListenerで第1引数に'message'を指定し、第2引数にコールバック関数を指定します。

messageイベントで実行するコード

(text) => {
  const response = text + 'こんにっちはー!';
  self.postMessage(response);
}

今回の例ではアロー関数でコールバック関数を指定しています。
受け取ったメッセージに文字列を結合してself.postMessageindex.jsにメッセージを送信しています。

index.js側でメッセージを受け取った時の処理を記述する

worker.addEventListener('message', (response) => {
  console.log(response);
});

worker.jsで行ったのと同じようにmessageを受け取った際の処理を記述できます。
例ではconsole.logに受け取ったメッセージを出力しているだけですが、受け取ったメッセージをどのように扱おうと自由です。

Workerで扱えないオブジェクト

ここまでで、簡単な例は紹介し終えましたが、一点注意点があります。
Workerの中ではDOMオブジェクトやwindowオブジェクトは利用することができません。
なので、受け取った値に応じてDOMの操作などを行う際は一度index.jsに値を返して行います。

その他、利用できる関数やAPIなどが下記に一覧で記載されているので参考にしましょう。
Web Worker available

まとめ

  • 非同期処理≠並列処理
  • 並列処理を行いたい場合はWeb Workerを使おう
  • JavaScriptは楽しい
  • JavaScriptは楽しい
  • JavaScriptは楽しい

以上です。

RecentPost