前端性能优化:使用 Web Workers 实现轮询
痛点
轮询是一种常见的客户端与服务器端通信的方法,通过定期发送 HTTP 请求来获取最新数据。这种方法虽然简单易实现,但在某些情况下会对主线程性能产生负面影响,比如需要兼容性能较差的手机和需要高性能页面不影响主进程时。
在面对性能较差的手机时,我们需要尽量的减少主线程的 CPU 占用,因为主线程需要进行 UI 渲染,用户操作。主线程被频繁占用可能导致页面响应变慢,影响用户体验。例如,用户可能会感觉到页面滚动不流畅,按钮点击反应迟缓等。
因此 我想结合之前说过的 webworker 来解决,利用 Web Workers 将轮询任务移出主线程。
Web Worker 为浏览器提供了多线程处理能力,允许在后台线程执行脚本,避免了长时间运行的脚本导致的页面失去响应。这意味着,像轮询这样的耗时任务可以委托给 Worker 线程处理,保证了用户界面的流畅性。
关于 webworker 可以参考我之前写的这篇 《你不要命啦?动态创建 Web Worker 还能这样用啊!》
实现步骤
- 创建 Worker 脚本:首先,定义一个包含轮询逻辑的 JavaScript 代码字符串,并将其封装进一个 Blob 对象。此 Blob 对象随后被用来创建一个新的 Worker 实例。
1const blob = new Blob(
2 [
3 `
4 let requestCount = 0
5 // 处理收到的消息
6 self.onmessage = function(event) {
7 ...
8 };
9 // 开始轮询函数
10 function startPolling(interval, url, data, headers) {
11 ...
12 }
13 `,
14 ],
15 { type: "application/javascript" }
16);
17
18const worker = new Worker(URL.createObjectURL(blob));
19
- 定义轮询函数:在 Worker 脚本中,定义
startPolling
函数,它负责执行实际的 HTTP 请求并设置下一次轮询的定时器。这样,一旦接收到主线程发来的启动命令,Worker 就会开始周期性地调用该函数。
1function startPolling(interval, url, data, headers) {
2 function poll() {
3 fetch(url, {
4 method: "POST",
5 headers: new Headers(headers),
6 body: JSON.stringify({ ...data }),
7 })
8 .then((response) => {
9 return response.json();
10 })
11 .then((res) => {
12 // 将请求结果发送回主线程
13 self.postMessage({ res, requestCount: requestCount++ });
14 })
15 .catch((error) => {
16 console.log("Request failed:", error);
17 });
18
19 // 调用自身以实现持续轮询
20 setTimeout(poll, interval);
21 }
22
23 // 立即执行一次
24 poll();
25}
26
- 配置与启动轮询:在主线程中,创建 Worker 实例后,通过
postMessage
方法向 Worker 发送初始化信息,包括轮询间隔、请求的 URL、请求头和数据。Worker 接收到这些信息后,开始执行轮询逻辑。
1worker.postMessage({
2 type: "start",
3 interval: 5000,
4 url: "http://192.168.110.145:18200/gateway/xxxxxx",
5 headers: {
6 "content-type": "application/json; charset=utf-8",
7 time: Base64.encode(serverTimeStamp().toString()),
8 accountId: Base64.encode(tipWords.userId) || "",
9 authToken: TOKEN,
10 requestId: getRandomNumberFn(),
11 },
12 data: {
13 secretCode: RsaAndAes.encrypt(Key),
14 encryptedData: RsaAndAes.encryptAes(saveData),
15 // userId: tipWords.userId
16 },
17}); // 每5秒轮询一次
18
-
处理响应与错误:Worker 内部的轮询函数通过 fetch API 发起请求,并处理响应或错误。成功获取到数据后,通过 self.postMessage 将结果传回主线程,以便进一步解密和处理。
-
定义终止条件,停止 webworker。
1worker.onmessage = function (event) {
2 if (event.data.requestCount > 2) {
3 worker.terminate();
4 }
5 // 业务逻辑
6};
7
实现代码
1// pollWorker.js
2import { Base64 } from 'js-base64';
3import RsaAndAes from '~/composables/RsaAndAes';
4import { getRandomNumberFn } from '~/composables/baseRequest';
5
6export function createWorker() {
7 const blob = new Blob(
8 [
9 `
10let requestCount = 0;
11// 处理收到的消息
12self.onmessage = function (event) {
13 if (event.data.type === "start") {
14 // 开始轮询
15 const interval = event.data.interval;
16 startPolling(interval, event.data.url, event.data.data, event.data.headers);
17 }
18};
19
20// 开始轮询函数
21function startPolling(interval, url, data, headers) {
22 function poll() {
23 fetch(url, {
24 method: "POST",
25 headers: new Headers(headers),
26 body: JSON.stringify({ ...data }),
27 })
28 .then((response) => {
29 return response.json();
30 })
31 .then((res) => {
32 // 将请求结果发送回主线程
33 self.postMessage({ res, requestCount: requestCount++ });
34 })
35 .catch((error) => {
36 console.log("Request failed:", error);
37 });
38
39 // 调用自身以实现持续轮询
40 setTimeout(poll, interval);
41 }
42
43 // 立即执行一次
44 poll();
45}
46 `
47 ],
48 { type: 'application/javascript' }
49 );
50
51 const worker = new Worker(URL.createObjectURL(blob));
52 // 将post函数传递给WebWorker
53
54 const TOKEN = '';
55 const saveData = JSON.parse(JSON.stringify({} || {}));
56 const config = {};
57 const interDomainName = '';
58 const ENV = '';
59 const nodeEnv = '';
60 const Key = ''; //存储公钥
61
62 // 发送开始消息给WebWorker,传递轮询间隔
63 worker.postMessage({
64 type: 'start',
65 interval: 5000,
66 url: 'http://192.168.110.145:18200/gateway/xxxxxxx',
67 headers: {
68 'content-type': 'application/json; charset=utf-8',
69 partnerId: 'MTAy',
70 time: '',
71 accountId: '',
72 countries: '',
73 authToken: TOKEN,
74 requestId: ''
75 },
76 data: {
77 secretCode: RsaAndAes.encrypt(Key),
78 encryptedData: RsaAndAes.encryptAes(saveData)
79 }
80 }); // 每5秒轮询一次
81 return worker;
82}
83
总结
使用 Web Workers 实现轮询能够显著改善前端性能,尤其是在移动设备或低性能设备上。通过将轮询任务移至后台线程,主线程得以专注于用户界面的渲染和交互,提高了应用的响应速度和流畅度。
优势:
- 将耗时的网络请求和数据处理任务从主线程移至 Web Worker,减少了对主线程的占用,使用户界面更流畅。
- 主线程的空闲时间增加,有助于提升滚动、点击等用户操作的响应速度,避免页面卡顿现象。
- ️ Worker 自身沙盒特性使项目更加安全可靠
相关笔记