简单记一次 Web Worker 的使用

515 阅读2分钟

前言

JavaScript 是单线程语言,代码的执行就是从上往下依次执行的,这就有可能会导致项目代码执行到某个地方时,因为某些原因而造成代码堵塞的问题,这也是 JS 异步产生的由来,但是并不是所有地方代码的处理都适用异步。

比如说数据可视化时,需要循环遍历大量数据(至少几十万条数据),但是又不能造成页面的阻塞(或卡死)

像这种情况,就可以用 Web Worker 来弄。

Web Worker 具体是干嘛用的,这里不赘述,访问下面参考链接看看就明白了,这里主要是记录下 Web Worker 是怎么使用的。

使用

环境: react + Web Worker

假设项目有个模块(或者是页面)ChartsPage,下面是一些页面相关的文件

ChartsPage |-index.tsx // worker 的使用文件 |-worker.js // worker 文件 |-其它如index.less、components等等,主要是上面两个文件

  • worker.js
/*
 * @Description: worker
 */

export const workerDeal = () => {
    onmessage = event => {
        const resData = event.data // ? 通过 onmessage 参数的 event.data 获取传过来的数据
        
        // 然后在这里做一些值的处理,如大数据量的数据的轮询处理等
        
        postMessage(resultData) // ? 通过 postMessage 的方式将处理好的数据回传回去
        
    }
}

/**
 * new Worker() 的参数不能直接是本地文件,所以需要将 worker 的方法转换一下
 */
export const getNewWorkerUrl = workerFun => {
    let code = workerFun.toString()
    code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))
    const blob = new Blob([code], { type: 'application/javascript' })
    return URL.createObjectURL(blob)
}
  • index.tsx
import { getNewWorkerUrl, workerDeal } from './worker'

const ChartsPage = (props) => {

  const worker = new Worker(getNewWorkerUrl(workerDeal)) // ? !!! 实例化,注意这里传入的值,必须转义一下

  useEffect(
    () => () => {
      // ? 离开当前页面,销毁 worker,不一定非要在这里销毁,页面中函数也可以。
      worker.terminate()
  	},[])

	const onClick = () => {
    worker.postMessage({ // ? 通过 worker.postMessage 的方式将数据传给 worker,可以直接是某个值,也可以是对象等
    	data, 
    })

    worker.onmessage = event => {
      const resultData = event.data   // ? 也是通过 event.data 的方式获取返回值
      console.log('返回值', resultData)
    }
  }

	return <div>页面内容</div>
}

1. axios.all() 的返回值,传入 worker 拿不到

问题:

axios
    .all(axiosArr)
    .then(response => {
    		console.log(response) // ? 在这能打印出返回的数据
        worker.postMessage(JSON.stringify(response)) // ? 但是从这传入给 worker,却拿不到数据
    })
    .catch(() => {})

解决:

axios
    .all(axiosArr)
    .then(response => {
        worker.postMessage(response) // ? 转换成字符串传入,然后在 worker 获取到并解析
    })
    .catch(() => {})

2. 不能使用拓展运算符(...)进行数组或对象的合并

问题:

处理数据,难免会遇到数组或对象的合并,通常我们常用的方式就是使用拓展运算符

const arr1 = [{}, {}]
const arr2 = [{}, {}]
const obj1 = {}
const obj2 = {}

const array = [...[], ...arr1, ...arr2]
const object = {...obj1, ...obj2}

但是在 worker 中这样使用,会报错

解决:

使用原生的数组,对象合并方式

const arr1 = [{}, {}]
const arr2 = [{}, {}]
const obj1 = {}
const obj2 = {}

const array = arr1.concat(arr2)
const object = Object.assign({}, obj1, obj2)

[0] Web Worker - MDN 文档

[1] 阮一峰 - Web Worker 使用教程

[2] 浏览器兼容性支持