Node.jsのWorker Threadsは、Workerが例外を投げるとメインスレッドも落ちるので注意

前回に引き続き、Node.jsのWorker Threadsについて解説していきます。

本稿では、もしもWorkerが例外を投げたらどうなるのかを検証します。

前回: [CPU負荷で3秒かかっていた処理を「Worker Threads」で1秒に時短する](https://qiita.com/suin/items/bce351c812603d413841)

## 本稿で分かること

* Worker Threadsでエラーが発生した場合、メインスレッド側はどうなるのか？
* Worker側の例外をメインスレッドでエラー処理できるのか？

## Workerで例外を起こすと？

まずは、Workerで例外を投げるとどうなるのか見ていきましょう。

メインスレッド側のコード、シンプルにWorkerを起動するだけにします:

```main.js
const {Worker} = require('worker_threads')
const worker = new Worker('./worker.js')
```

Worker側のコードは常に例外を投げるようにします:

```worker.js
throw new Error('Error was thrown in worker')
```

実行してみると、どうなるのか試してみます。

```shell_session
$ node main.js

events.js:293
      throw er; // Unhandled 'error' event
      ^
Error: Error was thrown in worker
    at Object.<anonymous> (/Volumes/dev/nodejs-playground/worker-threads/07-error-thrown-in-worker/worker.js:1:7)
    at Module._compile (internal/modules/cjs/loader.js:1147:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
    at Module.load (internal/modules/cjs/loader.js:996:32)
    at Function.Module._load (internal/modules/cjs/loader.js:896:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at MessagePort.<anonymous> (internal/main/worker_thread.js:167:24)
    at MessagePort.emit (events.js:316:20)
    at MessagePort.onmessage (internal/worker/io.js:78:8)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:11:10)
Emitted 'error' event on Worker instance at:
    at Worker.[kOnErrorMessage] (internal/worker.js:209:10)
    at Worker.[kOnMessage] (internal/worker.js:219:37)
    at MessagePort.<anonymous> (internal/worker.js:145:57)
    at MessagePort.emit (events.js:316:20)
    at MessagePort.onmessage (internal/worker/io.js:78:8)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:11:10)
```

何もエラーハンドリングをしないと、例外ログが出力されることがわかりました。

## Workerで例外が発生すると、メインスレッドも落ちる？

今度は、Workerで例外が発生し、メインスレッドで一切エラーハンドリングしなかったら、メインスレッドが落ちるのか試してみましょう。

メインスレッドの生死を確認できるように、main.jsの最後にログを出力するコードを追加します:

```main.js
const {Worker} = require('worker_threads')
const worker = new Worker('./worker.js')

// 追加↓
setTimeout(function () {
  console.log('The main thread is alive!')
}, 3000)
```

メインスレッドが生き残れたら、3秒後に「The main thread is alive!」が出力されるはずです。

さっそく動かしてみます:

```console
$ node main.js

events.js:293
      throw er; // Unhandled 'error' event
      ^
Error: Error was thrown in worker
    at Object.<anonymous> (/Volumes/dev/nodejs-playground/worker-threads/08-error-thrown-in-worker/worker.js:1:7)
...(略
```

なんと、先程と同じエラーログだけ出て、プロセスが3秒持たずに終了してしまいました。Workerの例外はメインスレッドのプログラムを停止させてしまうことがわかりました。

## Workerの例外をメインスレッドで補足するには？

Worker側で発生した例外を、メインスレッド側で補足するには、`error`イベントのハンドラを作る必要があります。イベントハンドラは、`Worker`クラスの`on`メソッドで登録します:

```js
worker.on('error', err => {
  // エラー処理
})
```

先程のmain.jsにエラー処理のコードを追加します:


```main.js
const {Worker} = require('worker_threads')
const worker = new Worker('./worker.js')

// 追加↓
worker.on('error', err => {
  console.error(err.message)
})

setTimeout(function () {
  console.log('The main thread is alive!')
}, 3000)
```

今度はこのmain.jsを実行してみて、メインスレッドが終了しないかを確認してみましょう。

```console
$ node main.js
Error was thrown in worker
The main thread is alive!
```

上の実行結果を見ると、1行目にWorker側で発生したエラーの内容が出ており、エラーハンドリングが効いているのがわかります。2行目からは、メインスレッドがエラー発生後も動作しつづけたことが確認できます。

## まとめ

以上の検証結果をまとめると……

* Workerで例外が発生したとき、メインスレッドでエラーハンドリングしないと、メインスレッドも死ぬ。
* メインスレッドでエラーハンドリングにするには、`error`イベントのハンドラを登録する。

### 所感

Worker側の例外がメインスレッドに致命傷を与えるのは意外だったので、エラーハンドリングはサボらずしたほうが絶対いいですね。