在H5 worker 系列一 基础知识提到过browserify/webworkify,原理可以参考WebWorker实战使用中,作者表示:
实际开发中我们不会把所有的代码都放在一个文件中让子线程加载,肯定会选择模块化开发。官方提供的方式是使用importScripts,但是这个在实际开发中很不实用,importScripts的加载方式是阻塞式的,所以我们最好用打包工具将所有worker中需要的文件打包成一个文件。这里我推荐browserify/webworkify,这是webpack的一个插件。对于webworkify-webpack的原理其实并没有使用importScripts而是使用另一种方式来创建worker,将js代码stringify后创建Blob对象,然后又createObjectURL创建对象url来实例化worker。类似如下过程:
image.png
我们在github上下载browserify/webworkify源码后,先运行一下npm i -D
安装一下环境,然后browserify example/main.js -o bundle.js
,就可以在index.html中使用bundle.js了,关于browserify,可以参考Browserify + watchify
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>browserify</title>
<script src="bundle.js"></script>
</head>
<body>
</body>
</html>
一、官方的使用Example
//main.js
var work = require('../');
var w = work(require('./worker.js'));
var first = true;
w.addEventListener('message', function (ev) {
if (first) {
// revoke the Object URL that was used to
// create this worker, so as not to leak it
URL.revokeObjectURL(w.objectURL);
first = false;
}
console.log(ev.data);
});
w.postMessage(4); // send the worker a message
//worker.js
var gamma = require('gamma');
module.exports = function (self) {
self.addEventListener('message',function (ev){
// ev.data=4 from main.js
var startNum = parseInt(ev.data);
setInterval(function () {
var r = startNum / Math.random() - 1;
self.postMessage([ startNum, r, gamma(r) ]);
}, 500);
});
};
二、hls.js demuxer.js的使用
在hls.js 源码解读【3】中提到
import work from 'webworkify-webpack';
if (config.enableWorker && (typeof (Worker) !== 'undefined')) {
logger.log('demuxing in webworker');
let w;
try {
w = this.w = work(require.resolve('../demux/demuxer-worker.js'));
this.onwmsg = this.onWorkerMessage.bind(this);
w.addEventListener('message', this.onwmsg);
w.onerror = function (event) {
hls.trigger(Event.ERROR, {
type: ErrorTypes.OTHER_ERROR,
details: ErrorDetails.INTERNAL_EXCEPTION,
fatal: true,
event: 'demuxerWorker',
err: {
message: event.message +
' (' + event.filename + ':' + event.lineno + ')'
}
});
};
w.postMessage({
cmd: 'init',
typeSupported: typeSupported,
vendor: vendor,
id: id,
config: JSON.stringify(config)
});
} catch (err) {
logger.error('error while initializing DemuxerWorker' +
', fallback on DemuxerInline');
if (w) {
// revoke the Object URL that was used to create
//demuxer worker, so as not to leak it
URL.revokeObjectURL(w.objectURL);
}
this.demuxer = new DemuxerInline(observer, typeSupported, config, vendor);
this.w = undefined;
}
} else {
this.demuxer = new DemuxerInline(observer, typeSupported, config, vendor);
}
onWorkerMessage(ev) {
let data = ev.data,
hls = this.hls;
//console.log('onWorkerMessage:' + data.event);
...
}
关于require.resolve:
// 用法
require.resolve('a.js')
// 返回 /home/ruanyf/tmp/a.js
简单的说,在 Node.js 中使用 fs 读取文件的时候,经常碰到要拼一个文件的绝对路径的问题 (fs 处理相对路径均以进程执行目录为准)。之前一直的方法都是,使用 path 模块以及 __dirname 变量 。代码如下所示:
fs.readFileSync(path.join(__dirname, './assets/some-file.txt'));
使用 require.resolve 可以简化这一过程fs.readFileSync(require.resolve('./assets/some-file.txt'));
此外, require.resolve 还会在拼接好路径之后检查该路径是否存在, 如果 resolve 的目标路径不存在, 就会抛出Cannot find module './some-file.txt'
的异常. 省略了一道检查文件是否存在的工序 (fs.exists).这个报错并不会加重你的检查负担, 毕竟使用 fs 去操作文件时, 如果发现文件不存在也会抛出异常. 反之, 通过 require.resovle 可以在提前在文件中作为常量定义, 那么在应用启动时就可以抛异常, 而不是等到具体操作文件的时候才抛异常.
三、flv.js
在transmuxer.js中能看到类似的写法
import TransmuxingWorker from './transmuxing-worker.js';
...
if (config.enableWorker && typeof (Worker) !== 'undefined') {
try {
let work = require('webworkify');
this._worker = work(TransmuxingWorker);
this._workerDestroying = false;
this._worker.addEventListener('message',
this._onWorkerMessage.bind(this));
this._worker.postMessage({
cmd: 'init',
param: [mediaDataSource, config]
});
this.e = {
onLoggingConfigChanged:
this._onLoggingConfigChanged.bind(this)
};
LoggingControl.registerListener(this.e.onLoggingConfigChanged);
this._worker.postMessage({
cmd: 'logging_config',
param: LoggingControl.getConfig()
});
} catch (error) {
Log.e(this.TAG, 'Error while initialize transmuxing worker' +
', fallback to inline transmuxing');
this._worker = null;
this._controller = new TransmuxingController(mediaDataSource, config);
}
} else {
this._controller = new TransmuxingController(mediaDataSource, config);
}
destroy() {
if (this._worker) {
if (!this._workerDestroying) {
this._workerDestroying = true;
this._worker.postMessage({ cmd: 'destroy' });
LoggingControl.removeListener(
this.e.onLoggingConfigChanged);
this.e = null;
}
} else {
this._controller.destroy();
this._controller = null;
}
this._emitter.removeAllListeners();
this._emitter = null;
}
然后就是transmuxing-worker.js里面各种通讯
/* post message to worker:
data: {
cmd: string
param: any
}
receive message from worker:
data: {
msg: string,
data: any
}
*/