问题:worker的串行加载问题
最近在开发wasm播放器,流媒体解析、音视频编解码都是重CPU的操作,如果在主线程完成,有可能会导致性能不佳。因此,打算采用worker来降低主线程的负担。
这个时候会遇到个问题,如果采用类似new Worker(workerFilePath)
的方式来初始化worker,会出现主文件、worker文件串行加载的问题。
做了个简单demo,github完整示例可点击这里。
// main.js
const worker = new Worker('worker.js');
worker.addEventListener('message', function(evt) {
console.log(`[main] result is: ${evt.data.result}.`);
}, false);
worker.postMessage({num1: 20, num2: 10});
console.log('[main] Main is initialized.');
// worker.js
self.addEventListener('message', function (evt) {
const num1 = evt.data.num1;
const num2 = evt.data.num2;
const result = num1 + num2;
console.log('[worker] num1=' + num1 + ', num2=' + num2);
self.postMessage({result: result});
}, false);
console.log(`[worker] Worker is initialized.`);
在浏览器里访问,网络请求如下,main.js、worker.js 出现串行加载情况。
从性能优化的角度来讲,串行加载往往是比较糟糕的实践,它有可能会抵消掉前面为了性能优化而付出的努力。
优化:解决worker串行加载的问题
最早写worker的科普文《【HTML5】webworker简介》,还是2013年的时候,这么多年过去了,从常理推断,类似的问题应该早有解决方案了。刚好前阵子把flv.js
的源码稍微撸了一遍,flv.js
支持启用worker,里面其实就有答案。
flv.js
源码分析跟本文无关,后面有时间打算专门写一篇,这里直接抛结论,关键伪代码如下:
const workerBlob = new Blob([workerFileContent], { type:'text/javascript' });
const workerObjectUrl = URL.createObjectURL(workerBlob);
const worker = new Worker(workerObjectUrl);
下面看具体源码,github完整示例可点击这里。
首先,worker.js 保持不变,只是main.js进行了微调:
const workerFileContent = `./worker.js`; // 注意,这行代码会通过脚本替换成实际的文件内容
const workerBlob = new Blob([workerFileContent], { type:'text/javascript' });
const workerObjectUrl = URL.createObjectURL(workerBlob);
const worker = new Worker(workerUrl);
worker.addEventListener('message', function(evt) {
console.log(`[main] result is: ${evt.data.result}.`);
}, false);
worker.postMessage({num1: 20, num2: 10});
console.log('[main] Main is initialized.');
转换脚本比较简单:
const fs = require('fs');
const workerFilePath = './worker.js';
const mainFilePath = './main.js';
const bundleFilePath = './bundle.js';
const workerFileContent = fs.readFileSync(workerFilePath, 'utf8').toString();
const mainFileContent = fs.readFileSync(mainFilePath, 'utf8').toString();
const bundleFileContent = mainFileContent.replace('./worker.js', workerFileContent);
fs.writeFileSync(bundleFilePath, bundleFileContent);
同样,在浏览器里查看效果,搞定。
相关链接
本文例子:https://github.com/chyingp/blog/tree/master/demo/2019.10.23-load-worker-via-objectUrl
URL.createObjectURL():https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL