我们都知道js是一个单线程的语言,当线程堵塞时,可能会导致页面无法正常交互,如一些复杂的可视化处理。即使是异步处理,也只是将其暂存到任务队列中去,等主线程执行完后依然会从任务队列中取过去。
为此,js提供了一种多线程效果的处理方式--webWorker,下面来用一段实力来展示:
基本示例(这里在一个vue项目中展示)
1.先创建一个webWorker对应的js文件
注:由于webworker构造函数接收的路径需要是线上的地址,因此在开发过程中可以放到public文件夹中(如example.js)这样,通过 项目启动地址+文件名即可访问该文件。
这样通过http://127.0.0.1:5173/example.js 即可访问该文件,并看到该文件内容:
2.在想要使用webWorker的方法中,创建一个webWorker对象(参数为上面的地址,本示例中的http://127.0.0.1:5173/example.js);不用本地地址的原因后续会说。
html
<button @click="startWebWorker">点我触发webWorker</button>
javascript
startWebWorker() {
// 创建一个webWorker对象
let myWorker = new Worker("http://127.0.0.1:5173/example.js")
},
这样,当点击此按钮时,会调用example.js中的脚本内容输出"hello world"。
3.postMessage方法:
通过postMessage可以向webWorker中传递参数(不可为函数):
javascript
startWebWorker() {
// 创建一个webWorker对象
let myWorker = new Worker("http://127.0.0.1:5173/example.js")
myWorker.postMessage("hello webWorker");
},
4.接收传过来的数据:
这里可以通过监听message事件来获取到数据。(webWorker对应文件中使用self进行postMessage等相关操作)
example.js中:
javascript
// console.log("hello world")
self.addEventListener("message", (res) => {
console.log(res.data); // 输出 hello webWorker
})
(ps,也可以使用self.onmessage来获取)
5.反向传递信息
其原理与上面的类似,同样使用postMessage,与监听message来实现:
示例vue文件中的方法:
javascript
startWebWorker() {
// 创建一个webWorker对象
let myWorker = new Worker("http://127.0.0.1:5173/example.js")
myWorker.postMessage("发送的信息:hello webWorker");
myWorker.addEventListener("message", (res) => {
console.log("返回的信息:" + res.data)
})
},
example.js中:
javascript
// console.log("hello world")
self.addEventListener("message", (res) => {
console.log(res.data); // 输出 hello webWorker
self.postMessage("nice to meet you too!");
})
结果:
6.补充:
比如说 public/example.js 中想要使用 public/test.js 中的方法:
public/test.js:
javascript
function testFun() {
console.log('我是testFun')
}
public/example.js:(这里直接这样引用会报错)
javascript
import "./test.js"
testFun();
需改为以下引发:
javascript
// import "./test.js"
importScripts("http://127.0.0.1:5173/test.js")
testFun()
当然有一种例外,就是test.js中使用的es6的module:
public/test.js:
javascript
export function testFun() {
console.log('我是testFun')
}
报错:Uncaught SyntaxError: Cannot use import statement outside a module
此时就不能用importScripts了,需要在创建webWorker对象时声明type为module
示例vue文件中的 创建一个webWorker对象 部分:
javascript
// 创建一个webWorker对象
let myWorker = new Worker("http://127.0.0.1:5173/example.js", {
type: "module"
})
public/example.js:
javascript
import { testFun } from "./test.js"
// importScripts("http://127.0.0.1:5173/test.js")
testFun()
这样就生效了。
webWorker注意事项
1.webWorker文件中的方法不可操作节点。
2.postMessage方法不可传递function方法。
3.new Worker参数不可为本地文件,可使用线上地址(如发布出去)。
4.在webWorker文件中若想使用第三方库的方法(如导出excel),可以通过cdn引入,或者将node_modules关键文件以同样方式(放入public或发布)引入。
单线程堵塞处理实例
1.堵塞简单示例,最简单的,遍历百亿次(电脑好可以更多,根据务必电脑情况斟酌):
html
<button @click="startWebWorker">遍历一亿次</button>
信息:<input />
javascript
startWebWorker() {
let total = 0
for(let i = 0; i< 10000000000; i++) {
total++;
}
console.log("结果是:" + total);
},
在结果输出前,你会发现输入框处于无法交互状态,整个页面都被堵塞所影响了,这个是异步也很难解决的问题。
因此,这里可以借助webWorker来实现:
javascript
startWebWorker() {
// 创建一个webWorker对象
let myWorker = new Worker("http://127.0.0.1:5173/example.js");
myWorker.postMessage("开始遍历");
myWorker.onmessage = (res) => {
console.log("结果是:" + res.data);
}
},
public/example.js:
javascript
self.addEventListener("message", (res) => {
if(res.data == "开始遍历") {
let total = 0
for(let i = 0; i< 10000000000; i++) {
total++;
}
self.postMessage(total)
}
})
这样,堵塞就因 webWorker多线程 的缘故而不会影响页面(如:输入框)的正常交互了。
希望本文会对您有所帮助 ^_^ ~