Web Workers 使用
Closed this issue · 0 comments
chenlong-io commented
单线程的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)。
专用线程只能由创建它的单个脚本使用,共享线程可以由多个脚本使用。
需要注意的点
- 有同源限制
- 无法使用 window 对象
- 无法访问 DOM 节点
浏览器支持情况
所以我们在使用它们时,不要忘记判断浏览器是否支持:
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>
