# Worker
> 使用 `Worker` 可以开启、调度其他线程处理一些耗时任务。但是它只兼容现代浏览器,所以,使用时需要先看系统部署的环境是否支持 [Worker 兼容性查询](https://caniuse.com/?search=Worker)
参考:[MDN-关于Worker](https://developer.mozilla.org/zh-CN/docs/Web/API/Worker)
## Worker url
根据文档所述,`Worker` 创建时支持 `blob url`。所以在 `Vue2` 项目中可以这样引入一个 `assets` 目录下的 `worker.js` (不推荐用这种写法)。
```javascript
// worker.js
export default function () {
self.addEventListener('message', function (e) {
console.log('worker 收到 message', e.data)
self.postMessage("hello")
}, false)
}
```
```vue
// test.vue
<script>
import workerScript from '@/assets/worker.js'
export default {
mounted() {
const blob = new Blob(`(${workerScript.toString()})()`, { type: 'text/javascript' })
const bloburl = URL.createObjectURL(blob)
const worker = new Worker(bloburl)
worker.postMessage({ type: 'test', data: 'hello' })
worker.onmessage = (eve) => {
console.log('收到 worker 发送的消息', eve.data)
}
}
}
</script>
```
## worker-loader
如果你使用的是 `Webpack` 项目,引入 `assets`目录下的 `worker` 文件时,用 `loader` 处理会更符合需求。毕竟大多时候,我们都需要在 `worker` 中引入其他包,来处理一些复杂逻辑(例如:用 `spark-md5` 计算文件 `hash` )
```js
// vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
open: true
},
chainWebpack: config => {
// 这是我的 /worker.js 文件,需要按需调整
config.module.rule('worker').test(/[\\/]worker\.js$/).use('worker-loader').loader('worker-loader').options({
inline: 'no-fallback'
})
}
})
```
```javascript
// worker.js 不再需要导出
self.addEventListener('message', function (e) {
console.log('worker 收到 message', e.data)
self.postMessage("hello")
}, false)
```
在组件中使用
```vue
// test.vue
<script>
import WorkerTest from '@/assets/worker?worker&url'
export default {
mounted() {
const worker = new WorkerTest()
worker.postMessage({ type: 'test', data: 'hello' })
worker.onmessage = (eve) => {
console.log('收到 worker 发送的消息', eve.data)
}
}
}
</script>
```
## 优化
```javascript
// utils.js
/**
* 源于数据库连接池的概念,用于管理多 Worker 调度问题
*/
class WorkerPool {
constructor(maxWorkers = 2) {
this.workers = []
this.running = 0
this.maxWorkers = maxWorkers
}
/**
*
* @param {() => Worker} createWorkerFunc
* @returns { Promise<{ worker: Worker, release: () => void }> }
*/
addWorker(createWorkerFunc) {
return new Promise((resolve, reject) => {
this.workers.push({ resolve, reject, createWorkerFunc })
this.run()
})
}
run() {
while (this.running < this.maxWorkers && this.workers.length > 0) {
const { resolve, reject, createWorkerFunc } = this.workers.shift()
try {
const worker = createWorkerFunc()
const release = () => {
worker.terminate()
this.running--
this.run()
}
resolve({ worker, release })
this.running++
} catch (error) {
reject(error)
}
}
}
}
export const MAX_WORKERS = navigator.hardwareConcurrency || 2
export const workerPool = new WorkerPool(MAX_WORKERS)
```
在组件中使用 `workerPool`
```vue
// test.vue
<script>
import WorkerTest from '@/assets/worker?worker&url'
import { MAX_WORKERS, workerPool } from '@/utils'
export default {
mounted() {
const worker = new WorkerTest()
worker.postMessage({ type: 'test', data: 'hello' })
worker.onmessage = (eve) => {
console.log('收到 worker 发送的消息', eve.data)
}
for (let i = 0; i < MAX_WORKERS; i++) {
workerPool.addWorker(() => new Worker()).then(({ worker, release }) => {
worker.postMessage("hello")
worker.onmessage = e => {
// 在 worker 使用完后释放资源
release()
}
})
}
}
}
</script>
```