Web Worker能否支持多事件响应?如何传递XHR请求进度至主线程?
Web Worker多事件支持与进度传递实现
当然没问题!Web Worker完全支持message、以及通过自定义消息模拟的progress类事件交互,而且你完全可以编写一个能响应多种不同需求的Worker脚本——核心思路就是在postMessage传递的消息里带上类型标识,让主线程能区分不同的消息用途。
先给你明确两个核心点:
- Web Worker本身没有内置的
progress事件,但我们可以在XHR请求的progress回调里,主动向主线程发送带进度标识的消息,实现进度同步。 - 一个Worker可以处理多种逻辑,只要主线程和Worker约定好消息的类型字段(比如
type: 'progress'或type: 'data'),就能分别响应不同的事件场景。
直接上可运行的示例代码
1. 主线程代码(页面脚本)
// 创建Worker实例 const geometryWorker = new Worker('geometry-worker.js'); // 监听Worker发来的所有消息,根据type区分处理 geometryWorker.onmessage = (e) => { const { type, data } = e.data; switch(type) { case 'progress': // 这里更新你的进度条或进度提示 console.log(`加载进度:${data.totalPercent}%,当前文件:${data.fileName}(${data.filePercent}%)`); break; case 'data': // 处理你原本的Geometry数据(ArrayBuffer形式) const { normal, position, index } = data; console.log('收到Geometry数据,开始加载'); // 这里写你原本的Geometry加载逻辑 break; case 'error': console.error('加载出错:', data); break; } }; // 监听Worker错误 geometryWorker.onerror = (error) => { console.error(`Worker出错:${error.message},行号:${error.lineno}`); }; // 给Worker发指令,开始加载指定的JSON文件列表 geometryWorker.postMessage({ type: 'load', files: ['normal.json', 'position.json', 'index.json'] });
2. Worker脚本(geometry-worker.js)
// 监听主线程的指令 self.onmessage = (e) => { const { type, files } = e.data; if (type === 'load') { // 批量加载多个JSON文件 loadMultipleFiles(files); } }; // 批量加载文件并统计总进度 async function loadMultipleFiles(fileList) { let totalCompletedFiles = 0; const totalFiles = fileList.length; for (const fileName of fileList) { try { const fileData = await loadSingleFile(fileName, (loaded, total) => { // 计算当前文件的加载进度,以及整体总进度 const filePercent = Math.round((loaded / total) * 100); const totalPercent = Math.round(((totalCompletedFiles + loaded/total) / totalFiles) * 100); // 向主线程发送进度消息 self.postMessage({ type: 'progress', data: { fileName, filePercent, totalPercent, loaded, total } }); }); // 处理加载完成的JSON数据,转成ArrayBuffer(根据你的实际数据结构调整) const bufferData = convertToBuffer(fileData); // 向主线程发送最终的Geometry数据,转移ArrayBuffer所有权优化性能 self.postMessage({ type: 'data', data: bufferData }, [bufferData.normal, bufferData.position, bufferData.index]); totalCompletedFiles += 1; } catch (err) { self.postMessage({ type: 'error', data: err.message }); } } } // 单个文件加载函数,带进度回调 function loadSingleFile(fileName, progressCallback) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', fileName); xhr.responseType = 'json'; // XHR原生progress事件,用来获取实时加载进度 xhr.onprogress = (e) => { if (e.lengthComputable) { progressCallback(e.loaded, e.total); } }; xhr.onload = () => { if (xhr.status === 200) { resolve(xhr.response); } else { reject(new Error(`加载${fileName}失败,状态码:${xhr.status}`)); } }; xhr.onerror = () => { reject(new Error(`加载${fileName}时发生网络错误`)); }; xhr.send(); }); } // 把JSON数组转换成ArrayBuffer(根据你的实际数据结构编写) function convertToBuffer(jsonData) { // 示例:假设jsonData包含normal、position、index数组 const normalBuffer = new Float32Array(jsonData.normal).buffer; const positionBuffer = new Float32Array(jsonData.position).buffer; const indexBuffer = new Uint16Array(jsonData.index).buffer; return { normal: normalBuffer, position: positionBuffer, index: indexBuffer }; }
代码关键点说明
- 消息类型约定:通过
type字段区分进度、数据、错误三种消息,主线程和Worker靠这个字段判断该执行什么逻辑。 - XHR进度传递:在Worker里的XHR
onprogress回调中,计算加载进度,通过postMessage把进度数据实时发给主线程。 - ArrayBuffer性能优化:传递ArrayBuffer时,使用
postMessage的第二个参数(转移列表),把缓冲区所有权转移给主线程,避免内存拷贝,提升性能。
这样你的Worker既能完成原本的Geometry数据加载,又能把每个文件的加载进度实时传递回主线程啦~
内容的提问来源于stack exchange,提问作者Joe C
相关产品推荐
相关产品推荐

