Web Worker 应用场景和实现

应用场景

   众所周知JavaScript是单线程的语言,所有任务只能在一个线程上完成,一次只能做一件事,即前面的任务还没有完成,后面的任务只能排队等待。如果前面的任务需要执行一些大数据量的计算,页面就会出现卡顿、点击无反应、甚至页面崩溃等现象。这对用户体验而言是非常糟糕的。

    在这种情况下,Web Worker可以为js提供一个多线程环境 ,主线程可以将一些耗时、复杂的计算任务分配给Worker线程 ,两者可以同时运行,互不干扰,等到Worker线程任务完成后,再把结果发送给主线程。这样Worker线程负责耗时、复杂的计算任务,主线程主要负责界面渲染不会被阻塞,使得用户体验更流畅

例如复杂的计算,上传大文件,请求耗时和计算耗时的都可以用

 方法介绍

主线程
1、Worker.onerror:指定 error 事件的监听函数。
2、Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
3、Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
4、Worker.postMessage():向 Worker 主线程发送消息。
5、Worker.terminate():立即终止 Worker 线程

子线程
1、self.name: Worker 的名字。该属性只读,由构造函数指定。
2、self.onmessage:指定message事件的监听函数。
3、self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
4、self.close():关闭 Worker 线程。
5、self.postMessage():向产生这个 Worker 主线程发送消息。
6、self.importScripts():加载 JS 脚本




vue 集成

npm i worker-loader -D

在vue.config.js中引入

config.module
      .rule('worker')
      .test(/\.worker\.js$/)// 以.worker.js结尾的文件将被worker-loader加载
      .use('worker-loader')
      .loader('worker-loader')
      .end();
    config.module.rule('js').exclude.add(/\.worker\.js$/)

Web Worker 应用场景和实现_第1张图片

创建js

以上传大文件的分片为例子 test.worker.js

import SparkMD5 from 'spark-md5';
import axios from "axios";
const CancelToken = axios.CancelToken;
let source = CancelToken.source();

//监听接收主线程 worker.postMessage发送的信息 chunkList uploadIdInfo 是主线程发送的数据 存放在e.data中
onmessage = async (e) => {
  //接收主线程参数
  const {chunkList,uploadIdInfo} = e.data;
  //执行分片上传逻辑
  const promises = chunkList.map(async (chunk) => {
    const response = await axios.put(chunk.uploadUrl, chunk.chunk.file, {
      onUploadProgress: 100,
      headers: {
        'Content-Type': 'application/octet-stream'
      },
      cancelToken: source.token,
    });
    if (response.status === 200) {
      console.log('分片:' + chunk.chunkNumber + ' 上传成功');
    } else {
      console.log('上传失败:' + response.status + ',' + response.statusText);
    }
    return response;
  });
  //等待全部请求完成
  const chunks = await Promise.all(promises);
  console.log("分片上传结果======="+chunks)

  //返回给主线程请求结果 
  postMessage(JSON.stringify(chunks));//返回给主线程一定要是基本类型,不是的转成json返回
};

页面引用

import  testWorker from "./test.worker";
/**
     * 上传分片文件
     * @param {*} chunkList
     */
    async uploadChunkBase(chunkList,uploadIdInfo) {
      const result = [];
      const self = this;
     //生成一个线程
      const worker = new testWorker()
     //像子线程发送数据
      worker.postMessage({chunkList,uploadIdInfo});
     //监听子线程返回结果
      worker.onmessage = e => {
        try {
          console.log("主线程回调")
          result[i] = e.data;//返回数据
          //销毁线程
          worker.terminate();
        } catch (error) {
          console.log(`Error processing the file in worker: ${error}`);
        }
      };
      worker.onerror = (error) => {
        console.log(`Worker encountered an error: ${error}`);
      };

说明

代码是上传大文件视频,分片去上传到minio文件服务器,因为分了太多分片,请求上传的时候页面会崩溃,所以分片上传改用多线程的方式,主要逻辑就是主线程生成一个worker 向子线程发送分片上传请求,子线程去执行上传分片

举个最简单的栗子

创建了一个aa.worker.js

//监听接收主线程发送的数据
onmessage = async (e) => {
  const {param1,param2} = e.data;
  console.log("主线程数据",param1,param2)
  let result = param1+param2;
  console.log("计算相加",result)
  //返回主线程计算结果
  postMessage(result);
};

需要用到的页面引用 

import  aaWorker from "./aa.worker";
      const aaWorker1 = new aaWorker()
      let param1 = 1;
      let param2 = 1;
      aaWorker1.postMessage({param1,param2});
      aaWorker1.onmessage = e => {
        try {
          let result = e.data;
          console.log("返回结果",result)
          //销毁
          aaWorker1.terminate();
        } catch (error) {
          console.log(`Error processing the file in worker: ${error}`);
        }
      };
      aaWorker1.onerror = (error) => {
        console.log(`Worker encountered an error: ${error}`);
      };

浏览器打印数据

参考文章:

浅析Web Worker及实践_webworker-CSDN博客

Web Worker 使用教程 - 阮一峰的网络日志

Webpack 下使用 web workers 及 基本原理 和 应用场景_webpack webworker-CSDN博客

https://webpack.html.cn/loaders/worker-loader.html

使用 Web Workers - Web API | MDN

你可能感兴趣的:(java,开发语言,前端)