还在让主线程忙到崩溃?用 Web Worker 解锁前端多线程的正确姿势!

一、Web Worker 介绍

JavaScript 是一种单线程语言,意味着它在一个时间点只能执行一个任务。尽管如此,一个进程可以包含多个线程,例如浏览器中的 Web Worker 提供了一种在后台线程中运行脚本的方式,从而避免主线程被阻塞。


1. Web Worker 的作用

Web Worker 是浏览器提供的一种多线程机制,允许开发者在后台线程中运行 JavaScript 脚本,主要用于以下场景:

  • 复杂计算:处理大量数据或复杂算法,避免阻塞主线程。
  • 后台任务:如文件处理、音频处理等。
  • 实时更新:如 WebSocket 数据处理、实时图表更新等。

2. 浏览器中的线程模型

谷歌浏览器是一个多进程架构的浏览器,每个进程可以包含多个线程。以下是常见的线程类型及其作用:

线程类型 主要任务
主线程 页面渲染、用户交互、JavaScript 执行
渲染线程 样式计算、布局计算、绘制和合成
工作线程 Web Worker、Service Worker 等后台任务
网络线程 HTTP 请求、WebSocket 通信
GPU 线程 硬件加速渲染、3D 绘制
定时器线程 setTimeoutsetInterval 等定时任务
I/O 线程 文件读写、数据库操作
音频线程 音频播放、音频处理

3. Web Worker 的使用示例

以下是一个简单的 Web Worker 示例,用于在后台线程中计算大数的阶乘:

new Worker(js文件)

主线程代码 main.js
const worker = new Worker('worker.js');

worker.postMessage(10); // 向 Worker 发送数据

worker.onmessage = (event) => {
    console.log(`阶乘结果: ${event.data}`);
};
worker.js
// 计算阶乘函数
function factorial(n) {
    if (n === 0 || n === 1) return 1;
    return n * factorial(n - 1);
}

// 接收主线程发送的数据
onmessage = (e) => {
    const result = factorial(e.data); // 计算 e.data 的阶乘
    postMessage(result); // 将结果发送回主线程
};
  • worker 运行在另一个全局上下文中,不同于当前的window
  • worker和主线程传递数据使用 postmessage,响应数据使用onmessage
  • worker 内不能操作dom节点,不能使用windows的方法和属性
  • worker 内部,worker 是有效的全局作用域,直接调用postmessage方法

4. 终止worker

worker.terminate()

5.错误处理

onerror =(meeage,filename,lineno)=>{

}

6.subworker

worker 能够生成更多的 worker

7.引入库和脚本 importScripts引入脚本

importScripts(); /* 什么都不引入 */
importScripts("foo.js"); /* 只引入 "foo.js" */
importScripts("foo.js", "bar.js"); /* 引入两个脚本 */
importScripts("//example.com/hello.js"); /* 你可以从其他来源导入脚本 */

8.共享worker

  const myWorker = new SharedWorker("worker.js");
  myWorker.port.postMessage([first.value, second.value]);


onconnect = (event)=>{
    const port = e.event[0]
    port.onmessage=(e)=>{
        // e.data
    }
}
  • 与一个共享 worker 通信必须通过 port 对象与 worker 通信
  • worker中使用onconnect处理函数来执行代码,
  • 使用事件的 ports 属性来获取端口并存储在变量中。
  • 为端口添加一个 onmessage 处理函数用来做运算并回传结果给主线程
  • worker 有它自己的执行上下文,worker 并不受限于创建它的 document(或者父级 worker)的内容安全策略

9. 其他类型的worker

  • ServiceWorkers 基本上是作为代理服务器,位于 web 应用程序、浏览器和网络(如果可用)之间。离线体验,拦截网络请求
  • Audio Worklet 提供了在 worklet(轻量级的 web worker)上下文中直接完成脚本化音频处理的可能性

10.调试worker线程

f12 下 source / worker可见

还在让主线程忙到崩溃?用 Web Worker 解锁前端多线程的正确姿势!_第1张图片

11. worker中可用的函数和接口

  • Navigator
  • fetch
  • Array Date Math String
  • setTimeout setInterval

在一个 worker 中最主要的你不能做的事情就是直接影响父页面。包括操作父页面的节点以及使用页面中的对象

参考 Web Worker API

二、 常用 Web Worker 库

1. react-useworker

react-useworker 是一个 React Hook 库,用于简化 Web Worker 的使用。它允许开发者轻松地将耗时的计算任务移到后台线程中,从而提高应用的性能。

使用方法
  1. 安装库:
    npm install react-useworker
    
  2. 示例代码:
    import useWorker from 'react-useworker';
    
    const App = () => {
         const [workerFn, { kill }] = useWorker((num) => {
              const factorial = (n) => (n <= 1 ? 1 : n * factorial(n - 1));
              return factorial(num);
         });
    
         const handleCalculate = async () => {
              const result = await workerFn(10);
              console.log(`阶乘结果: ${result}`);
         };
    
         return (
              <div>
                    <button onClick={handleCalculate}>计算阶乘</button>
                    <button onClick={kill}>终止 Worker</button>
              </div>
         );
    };
    
    export default App;
    

2. 使用 worker-loader

在 Vue 3 中,可以通过 worker-loader 和 Web Worker 来处理耗时任务。以下是一个使用 Web Worker 的 Vue 3 示例:

配置 Webpack

webpack.config.js 中添加以下配置:

module.exports = {
    module: {
        rules: [
             {
                 test: /\.worker\.js$/,
                 use: { loader: 'worker-loader' },
             },
        ],
    },
};
创建 Worker 文件

创建一个 Worker 文件 factorial.worker.js

// factorial.worker.js
onmessage = (e) => {
    const factorial = (n) => (n <= 1 ? 1 : n * factorial(n - 1));
    postMessage(factorial(e.data));
};
在 Vue 组件中使用

在 Vue 3 组件中使用 Web Worker:




这两个库可以帮助开发者更高效地使用 Web Worker,简化复杂任务的实现。

你可能感兴趣的:(工程开发,前端)