关于HTML5中的WebWorker

1.javascript的单线程问题

众所周知,js是不擅长计算的。因为计算是同步的,大规模的计算会让js主线程阻塞,主线程阻塞的结果就和界面完全卡死一样。

虽然javascript引入了异步概念,但异步只是把任务发布出去等着,后面还是会拉到主线程执行的,异步不可能在异步队列自己执行。所以一个耗时很高的操作,不管做不做异步,始终会卡死页面。下面是异步处理耗时高的操作的一个示例图:
在这里插入图片描述

2.WebWorker是什么?

为了给JavaScript 创造多线程环境,允许主线程创建 Worker线程,将一些任务分配给后者运行。
在这里插入图片描述

3.注意点

  • 同源限制
    分配给Worker线程运行的脚本文件,必须与主线程的脚本文件同源。
  • DOM 限制
    Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的DOM对象,也无法使用documentwindowparent这些对象。但是,Worker线程可以navigator对象和location对象。
  • 通信联系
    Worker线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
  • 脚本限制
    Worker线程不能执行alert()方法和confirm()方法,但可以使用XMLHttpRequest对象发出AJAX请求
  • 文件限制
    Worker线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

4.基本使用

下面使用Vue3作为实例。
base-webwoker.vue

<template>
  <span>webworker基本使用</span>
  <button @click="sendMessage">发消息给工人</button>
</template>

<script setup>
let worker1 = new Worker("http://localhost:5173/list.js");
worker1.addEventListener("message", (e) => {
  console.log(e.data); //2
});

const sendMessage = () => {
  worker1.postMessage("你好");
};
</script>

<style scoped></style>

注意为了做测试list文件必须写在public文件夹里面,才能保证同源。后面上线可以将相关webworker文件以cdn的形式提供,重要保证同源即可。
在这里插入图片描述
list.js

let a = 1 + 1;
self.postMessage(a);
self.addEventListener("message", (e) => {
  console.log(e.data);
  self.postMessage("收到");
});

然后运行项目,控制台一开始就会打印2:
在这里插入图片描述

点击发消息给工人按钮:

在这里插入图片描述

有些东西无法通过主线程传递给子线程。比如方法、dom节点、一些对象的特殊设置(freezegettersetter这些,所以vue的响应式对象是不能传的)。
在这里插入图片描述
在这里插入图片描述

再说说模块的引入问题:

public文件夹新建a.js

function a() {
  console.log("test");
}

我们想要在list.js中引入这个模块,这时候是不能通过import引入的:
在这里插入图片描述
在这里插入图片描述
需要用到importScripts:
在这里插入图片描述
在这里插入图片描述
这在引入一些第三方库CDN时候可以很有用,特别是那些比较旧的库,它们没有相应的模块化规范。

如果引入的文件是esm规范,这时候必须在new Worker()时候必须要说明:

export function test() {
  console.log("test");
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.应用场景

由于webworker在dom方法的限制,所以不能使用webworker进行多线程渲染(所以vue、react框架没有考虑用webworker)。webworker的常见应用是处理耗时计算操作

5.1 在线滤镜

随着webgl、canvas等的流行,前端有越来越多的可视化操作。如在线滤镜、在线绘图、web游戏等。这些东西十分消耗计算。

如下filter-image.vue

<template>
  <input />
  <button @click="handleImg">过滤</button>
  <canvas id="img-canvas" width="1200px" height="600px"></canvas>
</template>

<script setup>
import imageResource from "../assets/qd.jpg";
let canvas;
let ctx;
let img = new Image();
img.src = imageResource;
img.onload = function () {
  canvas = document.getElementById("img-canvas");
  ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0, 1200, 600);
};
function handleImg() {
  //读取所有像素点
  const imageData = ctx.getImageData(0, 0, 1200, 600);
  console.log(imageData);
  const data = imageData.data;
  //循环每个像素点
  for (let i = 0; i < imageData.data.length; i++) {
    for (let j = 0; j < 1000; j++) {
      if (imageData.data[i] != 255) {
        imageData.data[i] = Math.min(data[i] + j, 0);
      }
    }
  }
  ctx.putImageData(imageData, 0, 0);
}
</script>
<style scoped></style>

运行项目:
在这里插入图片描述
点击过滤按钮:
在这里插入图片描述

该操作需要2819ms,也就是2s。在点击过滤按钮之后立马在input中输入,在那2s多的时间内是无法输入的,因为该操作被阻塞了。这时候我们需要用到webworker

新建picworker.js文件:

self.addEventListener("message", (e) => {
  if (e.data.data.length) {
    let imageData = e.data;
    //循环每个像素点
    for (let i = 0; i < imageData.data.length; i++) {
      for (let j = 0; j < 1000; j++) {
        if (imageData.data[i] != 255) {
          imageData.data[i] = Math.min(imageData.data[i] + j, 0);
        }
      }
    }
    self.postMessage(imageData);
  }
});

修改filter-image.vue:

<script setup>
import imageResource from "../assets/qd.jpg";
let canvas;
let ctx;
let img = new Image();
img.src = imageResource;
img.onload = function () {
  canvas = document.getElementById("img-canvas");
  ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0, 1200, 600);
};
let worker1 = new Worker("http://localhost:5173/picworker.js");
worker1.addEventListener("message", (e) => {
  const imageData = e.data;
  ctx.putImageData(imageData, 0, 0);
});
function handleImg() {
  //读取所有像素点
  const imageData = ctx.getImageData(0, 0, 1200, 600);
  worker1.postMessage(imageData);
}
</script>

这样子就解决了阻塞问题。

5.2 处理巨量数据电子表单

有些后台管理系统会涉及到电子表单数据的大量计算,如10万条数据导出为excel表格。

安装xlsx:

npm i xlsx

如下excel-counter.vue

<template>
  <input />
  <button @click="exportExcel">导出</button>
</template>

<script setup>
import { utils, writeFile } from "xlsx";
let arr = [];
for (let i = 0; i < 100000; i++) {
  arr.push({
    id: i,
    name: `张三${i}`,
    location: `xxx大道${i}`,
    age: i,
    a: i * 2,
    b: i / 2,
    c: 1 + 2,
    d: 23,
    e: 111,
    f: 123,
  });
}
function exportExcel() {
  // 创建一个新的工作簿
  const workbook = utils.book_new();
  // 创建一个工作表 要求一个对象数组格式
  const sheet = utils.json_to_sheet(arr);
  // 把工作表添加到工作簿  Data为工作表名称
  utils.book_append_sheet(workbook, sheet, "Sheet1");
  writeFile(workbook, "test.xlsx");
}
</script>

<style scoped></style>

运行项目:
在这里插入图片描述
点击导出按钮,会发现导出这个Excel表格用了2s多:
在这里插入图片描述
在点击导出按钮之后立马在input中输入,在那2s多的时间内是无法输入的,因为该操作被阻塞了。这时候我们需要用到webworker

先去node_modules中找到xlsx.js文件:
在这里插入图片描述

将其复制到public文件夹,然后新建public/excelworker.js文件:

importScripts("./xlsx.js");
console.log(XLSX);
let arr = [];
for (let i = 0; i < 100000; i++) {
  arr.push({
    id: i,
    name: `张三${i}`,
    location: `xxx大道${i}`,
    age: i,
    a: i * 2,
    b: i / 2,
    c: 1 + 2,
    d: 23,
    e: 111,
    f: 123,
  });
}
self.addEventListener("message", (e) => {
  // 创建一个新的工作簿
  const workbook = XLSX.utils.book_new();
  // 创建一个工作表 要求一个对象数组格式
  const sheet = XLSX.utils.json_to_sheet(arr);
  // 把工作表添加到工作簿  Data为工作表名称
  XLSX.utils.book_append_sheet(workbook, sheet, "Sheet1");
  self.postMessage(workbook);
});

修改excel-counter.vue:

<template>
  <input />
  <button @click="exportExcel">导出</button>
</template>

<script setup>
import { writeFile } from "xlsx";

let worker1 = new Worker("http://localhost:5173/excelworker.js");
worker1.addEventListener("message", (e) => {
  const workbook = e.data;
  writeFile(workbook,"test.xlsx")
});
function exportExcel() {
  worker1.postMessage("")
}
</script>
<style scoped></style>

这样子就解决了这个阻塞问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳与星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值