【JS Web API】WebWorker 不阻塞 UI 的 JavaScript 多线程方案

2026-05-04 11:42:08

WebWorker 介绍

在浏览器里 JavaScript 本身被设计成单线程语言,初衷是为了避免多线程操作 DOM 时可能出现的复杂冲突问题。这种单线程模型有一个显著的短板:一旦遇到长时间运行的耗时任务,比如一个执行超过 50 毫秒的长任务,它就会阻塞住唯一的主线程。导致页面变得无法及时响应你的任何点击或输入,感到明显的卡顿。

Web Worker 是 HTML5 引入的浏览器多线程技术,它允许将计算密集型或长时间运行的任务从主线程中剥离出来,在后台的独立线程中运行。这样一来,主线程就能专注于处理用户交互和页面渲染,从而有效避免页面卡顿、无响应等性能问题。

常见的耗时任务有:

大量数据排序示例

当数据量较小(例如少于1000条)且业务逻辑相对简单时,Web Worker的通信开销可能超过其带来的性能收益。这是因为 Web Worker 与主线程之间的通信依赖于 postMessage API,数据传输需要进行结构化克隆(Structured Clone Algorithm),这个过程会带来序列化和反序列化的开销。对于小规模数据,这种通信成本可能大于直接在主线程上执行任务的耗时,因此这种情况下建议直接在主线程处理。

下面是一个典型的使用场景示例:创建100万个随机数并进行排序,将计算密集型任务交给 Web Worker 处理,主要流程如下:

生成后台线程 sort-worker.js 文件:

// sort-worker.js
self.onmessage = function(e) {
  const bigData = e.data
  
  // 执行排序操作(不会阻塞主线程)
  const sortedData = bigData.sort((a, b) => a - b)
  
  // 处理后返回排序结果
  self.postMessage(sortedData)
  self.close() // 完成任务后关闭 Worker
}

生成 index.html 文件用于测试:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button id="sortBtn">生成100万随机数并排序</button>
    <div id="result"></div>

    <script>
        // 创建 Web Worker 实例
        const worker = new Worker('./sort-worker.js');
        const btn = document.getElementById('sortBtn');

        btn.addEventListener('click', () => {
            // 生成100万个随机数
            const largeArray = Array.from({ length: 1000000 }, () =>
                Math.floor(Math.random() * 100000)
            );

            console.log('开始排序,主线程仍可操作...')
            const startTime = performance.now()

            // 发送大数据到 Worker
            worker.postMessage(largeArray)

            // 接收排序结果
            worker.onmessage = function (e) {
                const endTime = performance.now()
                console.log('排序完成!前10个数据:', e.data.slice(0, 10))
                console.log('排序用时:', (endTime - startTime).toFixed(2), 'ms')
                document.getElementById('result').innerText =
                    `排序完成,共处理 ${e.data.length} 条数据,用时 ${(endTime - startTime).toFixed(2)} ms`

                worker.terminate() // 释放资源
            }

            // 错误处理
            worker.onerror = function (error) {
                console.error(`Worker错误:${error.message} (行号:${error.lineno})`)
                worker.terminate()
            }
        })
    </script>
</body>

</html>

到目前为止,准备工作已经完成。需要注意的是,不能直接在浏览器中通过 file:// 协议打开 HTML 文件,因为 WebWorker 必须在 HTTPS 或 localhost 环境下才能正常注册。

接下来,我们使用 http-server 创建一个本地 Web 服务器环境:

$ npx http-server . -p 8080

然后在浏览器中访问 http://localhost:8080/index.html,点击按钮,过一会可以看到打印结果信息:

排序完成,共处理 1000000 条数据,用时 291.20 ms

返回首页

本文总阅读量  次
皖ICP备17026209号-3
总访问量: 
总访客量: