什么是webworker
-
Web Worker 是一种在浏览器中运行 JavaScript 脚本的机制,它可以将运算密集型的任务放在另一个线程中运行,以避免阻塞浏览器主线程,从而提高页面的响应速度和用户体验。
-
Web Worker 是 HTML5 引入的一项标准,它提供了一种在后台运行 JavaScript 脚本的方式,以避免阻塞浏览器的主线程。Web Worker 可以将计算密集型的任务放在另一个线程中运行,从而避免阻塞浏览器主线程,提高页面的响应速度和用户体验。
js执行顺序
-
js代码在执行的时候,会先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后,再将异步宏任务从队列中调入主线程执行,一直循环至所有的任务执行完毕(完成一次事件循环EventLoop)
-
宏任务:整体代码script、setTimeout、setInterval、setImmediate、i/o操作(输入输出,比如读取文件操作、网络请求)、ui render(dom渲染,即更改代码重新渲染dom的过程)、异步ajax等
-
微任务:Promise(then、catch、finally)、async/await、process.nextTick、Object.observe(⽤来实时监测js中对象的变化)、 MutationObserver(监听DOM树的变化)
setTimeout(function () {
console.log('1');
})
new Promise(function (resolve) {
console.log('2');
resolve();
}).then(function () {
console.log('3');
})
console.log('4');
//打印顺序 2 4 3 1
分析:
1、遇到setTimeout,异步宏任务将其放到宏任务列表中,命名为time1;
2、new Promise 在实例化过程中所执⾏的代码都是同步执⾏的( function 中的代码),输出2 ;
3、 将 Promise 中注册的回调函数放到微任务队列中,命名为 then1 ;
4、 执⾏同步任务 console.log(‘4’) ,输出 4 ,⾄此执⾏栈中的代码执⾏完毕;
5、 从微任务队列取出任务 then1 到主线程中,输出 3 ,⾄此微任务队列为空;
6、 从宏任务队列中取出任务 time1 到主线程中,输出 1 ,⾄此宏任务队列为空
web worker的使用
npm install worker-loader
先下载依赖
下面的配置在vue.config.js修改Webpack 的构建规则,
表示将 Web Worker 脚本内联到打包后的 JavaScript 文件中,避免了额外的网络请求和文件加载时间。- chainWebpack: config => {
config.module
.rule(‘worker-loader’)
.test(/.worker.js$/)
.use({
loader: ‘worker-loader’,
options: {
inline: true
}
})
.loader(‘worker-loader’)
.end()
}
使用
先在src目录下新建workers文件夹,接着在里面新建worker.js,在js文件里添加下面的测试代码
addEventListener("message", async (event) => {
const result = await processData(event.data);
postMessage(result);
});
function processData(item) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("3");
}, 1000);
});
}
然后在views底下建立一个.vue文件,去生成若干个web worker创建线程,配合promise.all可以等待加载结束,在worker.js里使用settimeout模拟,底下有一个2000可以自行修改,意思是生成几个web worker,我这里的是5个,可自行修改启用多条线程
<template>
<div ></div>
</template>
<script>
import Worker from "worker-loader!@/workers/worker";
export default defineComponent({
data() {
return {
worker: null,
};
},
async created() {
this.inquirewbWorker(); // 在组件创建时调用connect函数连接WebSocket
},
methods: {
createWorker() {
const worker = new Worker();
worker.onmessage = function (e) {
const { data } = e;
};
worker.onerror = function (error) {
console.error("Web Worker 发生错误:", error);
};
return worker;
},
async sendData(worker, data) {
worker.postMessage(data);
},
waitWorker(worker) {
return new Promise(function (resolve, reject) {
worker.onmessage = function (e) {
const { data } = e;
resolve(data);
};
worker.onerror = reject;
});
},
processData(item) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("3");
}, 1000);
});
},
async inquirewbWorker() {
const data = Array.from({ length: 10000 }, (_, i) => ({
id: i,
value: Math.random(),
})); // 生成10000个随机数
const fortime = new Date().getTime();
var resultmap;
data.map(async (item, i) => {
resultmap = await this.processData(event.data);
if (i === data.length - 1) {
console.log("forEach结束时间", new Date().getTime() - fortime);
}
});
// return
const datalist = data;
const numWorkers = Math.ceil(datalist.length / 2000);
const workers = Array.from({ length: numWorkers }, () =>
this.createWorker()
); // 创建 Web Worker 数组
console.log("numWorkers", numWorkers);
for (let i = 0; i < numWorkers; i++) {
const start = i * 2000;
const end = Math.min(start + 2000, datalist.length);
const dataChunk = datalist.slice(start, end);
this.sendData(workers[i], dataChunk);
}
const wbtime = new Date().getTime();
const results = await Promise.all(workers.map(this.waitWorker)); // 等待所有 Web Worker 完成
console.log("所有 Web Worker 完成:", new Date().getTime() - wbtime);
},
},
});
</script>
<style lang="scss" scoped>
.mapbox {
margin-bottom: 50px;
}
</style>
在页面中可以看到线程的数量,一万条数据量较少,差距可能不大,web worker需要计算量很大的数据才能看到明显区别,不然不建议进行使用,1W条数据差距大概在1秒,那么十万就是10秒,并且webworker可以开启多个线程优势
webworker跟异步的区别
这两个其实不是一个东西,上面提到js执行顺序的基础只是为方便理解。
- js是单线程
- webworker是可以支配多线程
通俗点讲
- 在js里用多个异步,注意js是单线程,类似于A去上班,路上买包子,发现包子没做好,直接先去上班,等打卡后再来取,
- webworker多线程,类似于A去上班,路上买包子,发现包子没做好,叫了个跑腿在那等,自已先去上班,等跑腿拿到了后,再送过来给A(相等于创建一个子线程,子线程完成合入到主线程)
- 执行环境不同:
异步是一种编程模式,可在单线程环境中使用,适用于任何情况下的异步操作。
Web Worker是在浏览器环境中特有的技术,用于在独立的线程中执行计算密集型任务,适用于大量计算但不影响主线程的情况。
-
使用方式不同:
异步编程可以通过回调函数、Promise、async/await等方式实现。
Web Worker需要通过脚本文件创建,并通过消息传递机制与主线程通信。 -
功能和用途不同:
异步编程用于解决长时间操作的阻塞问题,提高程序的响应性能和吞吐量。
Web Worker用于后台执行复杂的计算任务,不阻塞页面的渲染和用户交互。
webworker的优缺点
- 优点
1. 多线程执行: Web Worker在独立的线程中执行,不会阻塞主线程,可以充分利用多核处理器和系统资源,提高性能和响应速度。
后台计算任务: Web Worker适用于执行复杂的计算任务,可以在后台处理大量的数据计算、图像处理、数值模拟等操作,而不会干扰页面的渲染和用户交互。
消息传递机制: Web Worker与主线程之间通过消息传递机制进行通信,可以实现高效的数据交换和共享,便于处理异步任务和传递大量的数据。
提升用户体验: 通过将耗时的计算任务放在Web Worker中执行,可以提高页面的响应速度和流畅度,改善用户体验。 - 缺点:
无法操作DOM: Web Worker运行在与主线程分离的上下文中,无法直接操作DOM元素,因此不能通过Web Worker直接更新页面内容或响应用户交互。
文件加载限制: Web Worker在浏览器中作为独立的脚本文件加载,并受到同源策略的限制,必须与主线程的代码来自相同的域名下。
较高的内存消耗: 每个Web Worker都需要一定的资源和内存来维护,创建大量的Web Worker可能会占用大量的内存,对于资源有限的设备可能会造成性能问题。
复杂性和调试困难: 使用Web Worker需要编写额外的代码来管理消息传递和同步问题,增加了代码的复杂性,并且在调试时可能会出现一些困难。
webworker使用场景
- 大量请求:有限制,因为会受到跨域影响,需要在同个域进行,也无法通过jsonp来解决
- webworker没有dom跟window对象,目前大部分的第三方插件都会带有window对象,出现即报错
- 内部可以使用self访问webworker
- 复杂计算任务: Web Worker适用于执行复杂的计算任务,如大量数据的处理、图像处理、数值模拟等。通过将这些计算任务放在Web Worker中执行,可以充分利用多核处理器和系统资源,提高性能和响应速度。