chenlong-io/blog

Web Workers 使用

Closed this issue · 0 comments

单线程的JavaScript

从我接触 js 的时候,经常听到一句话:js 是单线程的。
单线程意味着 js 代码在执行时,只能按编码顺序从上到下执行(暂时抛开异步方法),如果遇到计算量大、耗时长的任务,用户就能感觉到卡顿。

JavaScript 的主线程主要作用是服务与 UI 构建,如果遇到繁重任务阻塞了 UI 主线程,就会感觉到卡。

一般我们解决的方法有两个:异步、使用Web Worker

异步暂时不讨论,这里主要说 Web Worker

Web Worker

既然主线程用于构建 UI,那么为了不阻塞 UI 构建,我们将繁重任务从主线程剥离出来放到其他线程里执行,不就OK了?

使用 Web Worker 可以将 js 运行在后台线程中,由于它独立于主线程,所以不会阻塞 UI 的构建

专用线程 和 共享线程

专用线程(Dedicated Web Worker) 和共享线程(Shared Web Worker)。
专用线程只能由创建它的单个脚本使用,共享线程可以由多个脚本使用。

需要注意的点

  1. 有同源限制
  2. 无法使用 window 对象
  3. 无法访问 DOM 节点

浏览器支持情况

目前统计,目前约有 97.48% 的浏览器支持专用线程

而共享线程只有大约 36.75% 的浏览器支持

所以我们在使用它们时,不要忘记判断浏览器是否支持:

if (Worker) {
    //...
}
if (ShareWorker) {
    //...
}

使用

由于共享线程浏览器支持情况较差,本章我们只介绍专用线程。

我们创建一个文件夹,并在里面创建 index.html 和 worker.js
目录如下:

.
├── index.html
└── worker.js

index.html 代码:

<input type="text" id="ipt" value="" />
<div id="result"></div>

<script>
    const ipt = document.querySelector('#ipt');
    const worker = new Worker('worker.js');
    
    ipt.onchange = function() {
      // 通过postMessage发送消息
      worker.postMessage({ number: this.value });
    };
    
    // 通过onmessage接收消息
    worker.onmessage = function(e) {
      document.querySelector('#result').innerHTML = e.data;
    };
</script>

worker.js 代码:

// 这里的 self 类似主线程中的 window
self.onmessage = function(e) {
  self.postMessage(e.data.number * 2);
};

处理错误

在主线程中处理错误:

    // 主线程
    worker.onerror = function () {
        // ...
    }
    
    // 主线程使用专用线程
    worker.onmessageerror = function () {
        // ...
    }

在专用线程中处理错误:

    // worker 线程
    onerror = function () {
    
    }

加载外部脚本

Web Worker 提供了 importScripts() 方法,能够将外部脚本文件加载到 Wroker 中。

importScript('script1.js')
importScript('script2.js')

// 上面写法等同于
importScript('script1.js','script2.js')

子线程

Worker 可以生成子 Worker,但有两点要注意:

  • 子 Worker 必须与父网页同源
  • 子 Worker 中的 URI 相对于父 Worker 所在的位置进行解析

嵌入式 Worker

目前没有一类标签可以使 Worker 的代码像 <script> 元素一样嵌入网页中,但我们可以通过 Blob() 将页面中的 Worker 代码进行解析。

<script id="worker" type="javascript/worker">
// 这段代码不会被 JS 引擎直接解析,因为类型是 'javascript/worker'

// 在这里写 Worker 线程的逻辑
</script>
<script>
    var workerScript = document.querySelector('#worker').textContent
    var blob = new Blob(workerScript, {type: "text/javascript"})
    var worker = new Worker(window.URL.createObjectURL(blob))
</script>

相关链接