Web Worker

0x01 概述

  • Web Worker 是 Web API 之一,能够在一个独立于 Web 应用程序主执行线程的后台线程中运行脚本操作

    • JavaScript 是单线程语言,Web Worker 可以单开一个线程,并通过消息与主线程进行通信
    • 特别地,Web Worker 线程不能访问全局属性,即不能访问全局 API 或修改 DOM,须通过向主线程发送消息实现
    • 消息中的数据均为深拷贝
  • 目的:在独立线程中执行费时的处理任务,使主线程(通常是 UI 线程)的运行不会被阻塞或放慢

  • 分类:

    1. 专用 Worker:单个脚本使用的 worker,上下文由 DedicatedWorkerGlobalScope 对象表示
    2. Shared Worker:由在不同窗口、iframe 等中运行的多个脚本使用的 worker,只要它们与 worker 在同一域中
      • 相比专用的 worker,脚本必须通过活动端口进行通信
    3. Service Worker:作为代理服务器,位于 Web 应用程序、浏览器和网络(如果可用)之间
      • 除开其他方面,主要用于创建有效的离线体验、拦截网络请求,以及根据网络是否可用采取合适的行动并更新驻留在服务器上的资源
  • 使用场景:

    1. 大量 CPU 密集型任务
    2. 任务可被独立拆分

    如:大文件分片上传

0x02 专用 Worker

  1. ./index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <title>Web Worker</title>
      </head>
      <body>
        <button onclick="worker()">专用 Worker</button>
    
        <script>
          function worker() {
            console.time("Worker"); // 启动计时器
    
            let counter = 0;
            for (var i = 0; i < 10; i++) {
              const worker = new Worker("./worker/script.js"); // 创建一个 worker
              worker.postMessage(40); // 发送消息给 worker
    
              // 接收 worker 的消息
              worker.addEventListener("message", (event) => {
                counter++;
                if (counter === 10) console.timeEnd("Worker"); // 结束计时器
                worker.terminate(); // 销毁 worker
              });
            }
          }
        </script>
      </body>
    </html>
    
  2. ./worker/script.js

    importScripts("../algorithm.js"); // 引入算法脚本
    
    // 监听主线程发送的消息
    self.addEventListener("message", (event) => {
      let number = event.data; // 接收主线程发送过来的数据
      let result = fibonacci(number); // 计算结果
      self.postMessage(result); // 将结果发送给主线程
    });
    
  3. ./algorithm.js

    // 通过计算斐波那契数列模拟大数据处理
    function fibonacci(n) {
      if (n <= 1) return n;
      return fibonacci(n - 1) + fibonacci(n - 2);
    }
    

0x03 Shared Worker

  1. ./index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <title>Web Worker</title>
      </head>
      <body>
        <button onclick="worker()">专用 Worker</button>
        <button onclick="shared()">Shared Worker</button>
    
        <script>
          function worker() {
            // ...
          }
    
          function shared() {
            console.time("Shared Worker"); // 启动计时器
    
            let counter = 0;
            for (var i = 0; i < 10; i++) {
              const worker = new SharedWorker("./shared/script.js"); // 创建一个 shared worker
              worker.port.postMessage(40); // 发送消息给 shared worker
    
              // 接收 shared worker 的消息
              worker.port.addEventListener("message", (event) => {
                counter++;
                if (counter === 10) console.timeEnd("Shared Worker"); // 结束计时器
                worker.port.close(); // 销毁 shared worker
              });
            }
          }
        </script>
      </body>
    </html>
    
  2. ./shared/script.js

    importScripts("../algorithm.js");
    
    // 连接到主线程的端口
    self.onconnect = (event) => {
      let port = event.ports[0]; // 获取连接的端口
      // 监听主线程发送的消息
      port.onmessage = (e) => {
        let number = e.data;
        let result = fibonacci(number);
        port.postMessage(result);
      };
    };
    

0x04 Service Worker

Service Worker 生命周期:

graph TB installing-->actived & Error actived-->idle<-->terminated & fetch/message
  1. ./index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <title>Web Worker</title>
      </head>
      <body>
        <button onclick="worker()">专用 Worker</button>
        <button onclick="shared()">Shared Worker</button>
        <fieldset>
          <legend>Service Worker</legend>
          <button onclick="service()">注册</button>
          <button id="send-button">发送</button>
        </fieldset>
    
        <script>
          function worker() {
            // ...
          }
    
          function shared() {
            // ...
          }
    
          async function service() {
            if (!"serviceWorker" in navigator)
              return console.error("Service Workers 不受当前浏览器支持");
    
            // 注册 Service Worker
            const registration = await navigator.serviceWorker.register(
              "./service/script.js"
            );
            console.log("Service worker 注册范围: ", registration.scope);
    
            // 监听从 service worker 线程发送到主线程的 message
            navigator.serviceWorker.addEventListener("message", (event) =>
              console.log("service worker 传到主线程的消息: ", event.data, event)
            );
    
            // 发送消息到 service worker
            document
              .getElementById("send-button")
              .addEventListener("click", () =>
                navigator.serviceWorker.controller.postMessage({ id: 4 })
              );
          }
        </script>
      </body>
    </html>
    
  2. ./service/script.js

    // 注册 Service Worker
    self.addEventListener("install", (event) => {
      console.log("触发 install 事件: ", event);
    
      // 在安装阶段缓存资源
      // 使用 event.waitUntil 确保在安装完成前不会激活 Service Worker
      event.waitUntil(
        caches
          .open("v1") // 打开或创建一个名为 "v1" 的缓存
          .then((cache) => cache.addAll(["/webworker/service/data.json"])) // 将指定的资源添加到缓存中
      );
      console.log("Service Worker 安装完成,资源已缓存");
    });
    
    // 激活 Service Worker
    self.addEventListener("activate", (event) => {
      console.log("触发 activate 事件: ", event);
    });
    
    // 监听 fetch 事件
    self.addEventListener("fetch", async (event) => {
      console.log("触发 fetch 事件: ", event);
    
      const response = await caches.match(event.request.url); // 尝试从缓存中获取请求的资源
      if (response) return response;
      else return fetch(event.request); // 如果缓存中没有,则从网络获取
    });
    
    // 监听 message 事件
    self.addEventListener("message", (event) => {
      console.log("触发 message 事件: ", event);
    
      event.data.id++; // 修改数据中的 id 字段
      setTimeout(() => event.source.postMessage(event.data), 5000); // 5 秒后将修改后的数据发送回去
    });
    
  3. ./service/data.json

    [
      {
        "id": "1",
        "name": "Alice"
      },
      {
        "id": "2",
        "name": "Bob"
      },
      {
        "id": "3",
        "name": "Charlie"
      }
    ]
    

0x04 Web Worker in NodeJS

  • 通过 NodeJS 提供的 worker_threads 依赖实现
  • 语法与专用 WorkerShared Worker 类似

代码参考自《/sunwu51/gitpod/tree/worker | Github-sunwu51

-End-

posted @ 2025-06-01 00:52  SRIGT  阅读(29)  评论(0)    收藏  举报