众所周知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 脚本
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$/)
以上传大文件的分片为例子 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