浏览器多线程

WebWorker

阮一峰 Web Worker 使用教程

Web Worker的由来:

  1. 默认浏览器中运行的 JavaScript 是单线程,Web Worker 的作用就是为 JS 创建多线程环境,允许主线程创建 Web Worker 线程,并负责计算密集型或高延迟的任务。Web Worker 线程负责计算密集型或高延迟的任务
  2. 主线程只需要负责 UI 交互,这样页面更加流畅

Web Worker 注意事项:

  1. 同源限制:Web Worker 只能接受同源下的 JS 分配的任务
  2. DOM限制:Web Worker 无法读取和操作 DOM 对象,无法使用 document、window、parent 这些对象,但可以使用 navigator 和 location。
  3. 无法使用 window对象也意味着无法使用 window.setInterval()、无法操作 CSS、SVG、Canvas 等
  4. 通信限制:Web Worker 只能通过消息与主线程 JS 通信。
  5. 脚本限制:Web Worker 不能执行 alert()、confirm(),但可以使用 XMLHttpRequest对象发出 AJAX 请求。
  6. 文件限制:Web Worker 不能读取本地文件,即不能打开本地文件系统(file://),所加载的脚本必须来自网络。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
Worker 不能自动关闭,需要我们手动关闭
Worker 可以在子线程通过self.close()进行关闭,也可以在主线程通过worker.terminate()进行关闭
我们可以通过 Chrome 和开发者工具中的 Sources 标签下的 Page 树来观察 Worker 的状态,以确定 Worker 是否被成功创建/关闭。
在主线程中使用时,onmessage和postMessage() 必须挂在 worker 对象上,而在 worker 中使用时不用这样做。原因是,在 worker 内部,worker 是有效的全局作用域。
*/

// ./worker/intensiveTask.ts 线程任务

const self: Worker = globalThis as any;
//要把业务代码封装在一个函数里
const workerCode = () => {
//收到主线程的消息
self.onmessage = function (e) {
const data = e.data
console.log("线程收到消息"+data)
}
setTimeout(() => {
//发送消息给主线程
self.postMessage("Worker Thread: Hi");
//在子线程里只能用 close 方法
//globalThis.close()

}, 2000);
};

//因为 web worker 不能加载本地的文件,通过 URL.createObjectURL 伪装成 url
let code = workerCode.toString();
code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}"));
const blob = new Blob([code], { type: "application/javascript" });
const worker_script = URL.createObjectURL(blob);
console.log(worker_script);
export default worker_script;

主线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// url blob:http://localhost:3000/28adebcd-f120-45a8-9a55-49e9468a1ecc
//线程收到消息Main Thread: Hello
// 主线程收到消息Worker Thread: Hi

import IntensiveTask from './worker/intensiveTask';

useEffect(() => {
const myWorker = new Worker(IntensiveTask);

// When you want to get messages from Worker Thread
myWorker.onmessage = (message: any) => {
console.log("主线程收到消息" + message.data);
//线程自己不会停止,要手动停止以免浪费线程资源
myWorker.terminate()
};

// When you want to send messages to worker thread
myWorker.postMessage('Main Thread: Hello');
}, [])

webWorkerThread