⏲️

JavaScriptのWorkerをPromiseにしてみる

2023/08/20に公開

WorkerはJavaScriptでマルチスレッドを使うための手段になります。

JavaScriptは通常、たった1つのイベントループによりタスクが一つづつ実行されます。Workerは2つ目のイベントループを作ることができます。もちろん3つ目以降も作れます。

1つ目のイベントループではDOM操作が実行されているため、たとえばWasmを使うような重たい処理があると画面がフリーズすることがあります。そういったときにWorkerが活躍します。

とはいっても、JavaScriptに慣れているとイベントループは1つだけの方がプログラミングしやすいです。そこでWorkerをPromiseにできればfetch()関数を使うような使用感でWorkerを使うことができるようになります。

これを行うライブラリもありますが、難しい仕組みでは無いため、コピペできるcreateWork()関数というのを作りました。

使い方

Workerを作ってcreateWork()関数に渡すと、データを受け取って結果をPromiseで返す関数ができます。

const work = createWork(new Worker('worker.js'))

あとは、それを実行してawaitthenを使って結果を待つだけです。

const result = await work(6) // 7

Workerファイル

createWork()関数を使うには、Workerファイルを少し工夫する必要があります。

通信を行うデータはidとデータのタプルとし、idを受け取ってidを返すように書き直す必要があります。

例えばこれは、データを+ 1するWorkerです。

globalThis.addEventListener('message', event => {
  const [id, data] = event.data
  const result = data + 1
  globalThis.postMessage([id, result]);
})

createWork()関数

最後に、createWork()関数の実装です。コピペして使っていただいて問題ございません。

function createWork(worker) {
  const map = new Map()
  let id = 0

  worker.onmessage = event => {
    const [id, result] = event.data;
    const resolve = map.get(id)
    map.delete(id)
    resolve(result)
  }

  return data => new Promise(resolve => {
    map.set(id, resolve)
    worker.postMessage([id++, data])
  })
}

Discussion