首先,这是一篇不严谨的博文。
本文仅供参考,无法在实际生产中运用。
在捯饬文件分片上传的过程中,我总是觉得单线程太慢了。
于是想,用webWorker呗。
首先,我们可以查看一下WebWorker的兼容性。根据MDN文档来看,主流浏览器都兼容。
但有个问题,MDN表示WebWorker只支持到IE10.如果要兼容到ie8,势必要维护两份上传代码,怎么破???
发挥我们聪明才智的时候到了
好了,我们可以开始开脑洞了,
要声明的是,我们只是实现Worker这个假的构造函数。
首先,WebWorker在书写逻辑上是同步的,但其实加载Worker这个行为是异步的。
其次,WebWorker依赖事件与主线程进行沟通,中间经过的数据会被toString()。
再者,WebWorker的api比较简单,模拟起来也比较方便。
首先来看Worker的interface定义
[Constructor(USVString scriptURL, optional WorkerOptions options), Exposed=(Window,Worker)]
interface Worker : EventTarget {
void terminate();
void postMessage(any message, optional sequence<object> transfer = []);
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
};
dictionary WorkerOptions {
WorkerType type = "classic";
RequestCredentials credentials = "omit"; // credentials is only used if type is "module"
DOMString name = "";
};
enum WorkerType { "classic", "module" };
Worker includes AbstractWorker;
Worker接口继承了EventTarget,所以还能使用addEventListener removeEventListener这俩api
所以我们要实现一个假的Worker,需要实现以下内容:
-
加载Worker脚本
- 加载规则为同域名,从根目录开始寻找。
- 暂不做blob生成,对本篇文章主题而言没有意义。
-
实现以下内容
- 实现onmessage
- 实现onmessageerror (文章中暂不实现)
- 实现close
- 实现terminate (文章中暂不实现)
- 实现addEventListener (文章中暂不实现)
- 实现removeEventListener (文章中暂不实现)
- 实现个假事件。
一步一步来
为了模拟Worker的同步书写方式
我们需要实现一个同步加载脚本的方法 (●゚ω゚●)
注:在现代浏览器里已经不赞成使用了
(function (root) {
var cache = {};
// debug
window.__rc = cache;
var module = {};
module.exports = null;
var config = {
rootPath: ''
};
function hash(str) {
var hash = 5381,
i = str.length;
while(i) {
hash = (hash * 33) ^ str.charCodeAt(--i);
}
/* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
* integers. Since we want the results to be always positive, convert the
* signed int to an unsigned by doing an unsigned bitshift. */
return hash >>> 0;
}
var defaultShimFunction = function (code, f) {
if (f) {
return f.replace('[[code]]', code);
}
return code;
};
root.require = function (scriptPath, shim) {
var path = scriptPath;
shim = shim || '';
if (/^(\/|https?:\/\/)/.test(scriptPath) !== true) {
path = config.rootPath + scriptPath;
}
if (/\.js$/.test(path) === false) {
path += '/index.js';
}
var keyName = path + '?hash=' + hash(shim);
if (cache[path]) {
if (cache[keyName]) {
return cache[keyName]
}
eval(defaultShimFunction(cache[path], shim));
return cache[keyName] = module.exports;
}
var request = new XMLHttpRequest();
request.open('GET', path, false);
request.send(null);
if (request.status === 200) {
cache[path] = request.responseText;
eval(defaultShimFunction(cache[path], shim));
cache[keyName] = module.exports;
}
return cache[keyName];
};
root.require.config = function (opts) {
for (var i in opts) {
config[i] = opts[i]
}
};
}(window));
(๑´ㅂ`๑)上述代码不要在意,主要实现的功能其实就是特别智障的在浏览器里同步加载commonjs模块。。。
加了个垫片,方便操作
万事具备,只欠东风
来吧,直接上代码,worker-ployfill.js
window.__wbus = {
_lis: {},
on: function (evt, fn) {
this._lis[evt] = fn;
},
off: function (evt) {
if (!evt) {
this._lis = {};
return;
}
delete this._lis[evt];
delete this._lis['close' + evt];
},
emit: function (evt, data) {
this._lis[evt] && this._lis[evt](data);
}
};
function copy (a) {
if (typeof a === 'object') {
var c = JSON.stringify(a);
return JSON.parse(c);
}
return a;
}
function FakeEvent (data, id) {
this.data = copy(data);
this.type = 'message';
this.id = id;
}
var uuid = 1;
var FakeWorker = function (p) {
var self = this;
self._id = uuid++;
window.__wbus.on(self._id, function (data) {
var Fe = new FakeEvent(data, self._id);
self.onmessage && self.onmessage.call(Fe, Fe);
});
window.__wbus.on('close' + self._id, function (data) {
self.terminates();
});
var path = location.protocol + '//' + location.host + '/' + p;
var wf = require(path, '
module.exports = function () {\
var self = {};\
self.postMessage = function (data) {\
window.__wbus.emit(' + self._id + ', data);\
};\
self.onmessage = function () {};\
self.close = function () { window.__wbus.emit(close' + self._id + ') };\
[[code]]\
;return self;\
}\
');
this.worker = wf();
};
FakeWorker.prototype.onmessage = function () {
};
FakeWorker.prototype.postMessage = function (data) {
if (!this.worker) {
throw new ReferenceError('Worker already stoped!');
}
var d = new FakeEvent(data, this._id);
this.worker.onmessage && this.worker.onmessage.call(d, d);
};
FakeWorker.prototype.terminates = function () {
this.worker = null;
window.__wbus.off(this._id);
};
module.exports = FakeWorker;
智障儿童欢乐多。
来,让我们玩一把吧,下面是测试代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试23333</title>
</head>
<body>
<span>
{{text}}
</span>
<script type="text/javascript" src="/static/js/require.js"></script>
<script type="text/javascript">
require.config({
rootPath: '/node_modules/'
});
</script>
<script type="text/javascript">
var Worker = require('/static/js/worker-ployfill.js');
var cpus = require('cpus/browser.js')().length;
var workers = [];
for (var i = 0; i < cpus; i ++) {
workers[i] = new Worker('static/js/worker-test.js');
}
workers.forEach((item) => {
item.onmessage = function (e) {
console.log('main thread'),
console.log('thread id: %s', e.id);
console.log('msg: %s', JSON.stringify(e.data));
};
item.postMessage({
message: 'message from main'
});
})
</script>
</body>
</html>
// worker-test.js
self.onmessage = function (e) {
console.log('worker thread'),
console.log('thread id: %s', e.id);
console.log('msg: %s', JSON.stringify(e.data));
self.postMessage({
message: 'from FackWorker'
});
};
你看,智障系列
233333333.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。