LoginSignup
11
6

More than 3 years have passed since last update.

Vuex のアクションの処理を Web Worker にやらせる

Posted at

はじめに

Worker を未だに使う機会が無い(むしろ私が実用性を分かってない)ので、今後のために勉強がてら Vuex のアクションで行ってる処理を Worker へ移行する実装をしてみます
なんとなく理解できたら、次はworker-domを使ってみたいなと思っております
ちなみに Vuex のアクションはデフォルトで非同期処理をサポートしており、非同期処理で捌けるケースがほとんどだと思うので、Worker を使って何かを行うケースはあまり無いかもしれません
ただし、重い計算処理があり、非同期による並行処理では解決できない場合(処理を分割して処理速度を改善したいケース)やそもそも非同期処理を使いたくないケースなどがあれば Worker スレッドによる並列処理は良いと思います

私が携わったプロジェクトでは Vuex のアクションは主に API サーバにリクエストを投げて、DB から取得したデータを受け取って、Mutation にコミットする処理をしています

今回なぜ Vuex のアクションを題材にしたかというと、普段仕事で Vue+Vuex を使っているのもありますが、アクションを呼ぶ度に Worker で処理させるような実装であればイメージしやすく、なんとなく作りやすそうだなって思ったからです

実装の方針に関して

「vue web worker」とかで検索するとまずvue-workerというライブラリがヒットすると思います
vue-worker の実装はほぼ無くてsimple-web-workerというライブラリを Vue.prototype に$workerという名前で追加しているだけです
simple-web-worker の方は Web Worker を操作するユーティリティ関数をいくつか提供してくれてて、とりあえずrunの実装を見てみると Worker に postMessage して Promise でラップして返してくれてるようです

便利そうですが、ライブラリの更新が止まってる感じがするので、今回は素の Web Worker だけで実装しようと思います

worker-loader を使う

Worker をworker-loaderworker-pluginを使ってモジュール化して使う方法が便利そうです
worker-loader の処理でモジュール化された Worker を import して使えるようになります
手軽さと webpack の alias 設定が効くので worker-loader を採用します

webpack.config.js
module: {
  rules: [
    {
      test: /\.worker\.ts$/,
      use: ['worker-loader']
    }
  ]
}

一応補足ですが TS や ES で書いた Worker スクリプトを worker-loader だけでトランスパイルなどはできないので、別途以下のような loader 設定も必要です

webpack.config.js
module: {
  rules: [
    {
      test: /\.worker\.ts$/,
      use: ['worker-loader']
    },
    // TSのトランスパイルやLintなどのloaderは別途追加しておく
    {
      test: /\.ts$/,
      use: ['ts-loader', 'tslint-loader'],
      exclude: /node_modules/
    }
  ]
}

簡単な Worker の実装

とりあえず Vue から Worker への送受信の簡単な実装を試してみます

App.vue
<script lang="ts">
import Worker from '@/worker/sample.worker'
import Vue from 'vue'

export default Vue.extend({
  name: 'App',
  created() {
    const w = new Worker()

    w.postMessage({ test: 'Send from main thread' })
    w.addEventListener('message', e => console.log(e.data))
  }
})
</script>
widget-reports.worker.ts
const w: Worker = self as any
w.postMessage({ test: 'Send from worker thread' })
w.addEventListener('message', e => console.log(e.data))

それぞれのスレッド上で実行された console.log が出力されると思います

また、ブラウザの開発ツールなどでネットワークを見ると Worker が動いていることが確認できます
Sample.png

Vuex のアクションの実装

API 経由で DB からレポートデータを取得するアクションを想定して、実装しています

  1. 日付パラメータを変えながらアクションを 4 回リクエスト
  2. アクションで Worker インスタンスを作成
  3. Worker スレッドで API リクエストを投げ、色々データ加工処理とかして、メインスレッドへ返す
  4. メインスレッドで Mutation.commit する

アクションをパラメータを変えながら 4 回呼ぶ

App.vue
<script lang="ts">
import Vue from 'vue'
import { mapActions } from 'vuex'

export default Vue.extend({
  name: 'App',
  methods: {
    ...mapActions({
      getReport: 'GET_REPORT'
    })
  },
  created() {
    ;[
      { date: { from: '2019-01-01', to: '2019-01-02' } },
      { date: { from: '2019-01-03', to: '2019-01-04' } },
      { date: { from: '2019-01-05', to: '2019-01-06' } },
      { date: { from: '2019-01-07', to: '2019-01-08' } }
    ].forEach(this.getReport)
  }
})
</script>

アクションではアクションが呼ばれる度に Worker スレッドを起動し、パラメータを渡しています

action.ts
import SampleWorker from '@/worker/sample.worker'
import { ActionContext, ActionTree } from 'vuex'

const actions: ActionTree<any, any> = {
  ['GET_REPORT'](
    context: ActionContext<any, any>,
    date: { from: string, to: string }
  ) {
    const params = {
      from_date: date.from,
      to_date: date.to
    }

    const w: Worker = new SampleWorker()
    w.postMessage({ params })
    w.addEventListener('message', e => {
      context.commit('GET_REPORT', e.data.reports)
      w.terminate()
    })
  }
}

export default actions

Worker では受け取ったパラメータを使って API リクエストを投げて、受け取ったデータをメインスレッドへ返す

sample.worker.ts
import axios from 'axios'

const w: Worker = self as any

w.addEventListener('message', e => {
  axios
    .get('https://example.co.jp/reports', {
      params: e.data.params
    })
    .then(res => {
      const reports = res.data.reports // ここで色々加工処理的なことをやる
      w.postMessage({ reports })
    })
    .catch(err => console.error(err.response))
})
11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6