据我所知,web worker需要写在一个单独的JavaScript文件中,并像这样调用:
new Worker('longrunning.js')
我正在使用闭包编译器来合并和缩小我所有的JavaScript源代码,我不希望将我的worker放在单独的文件中进行分发。有什么办法可以做到吗?
new Worker(function() {
//Long-running work here
});
既然一级函数对JavaScript如此重要,为什么标准的后台工作方式必须从web服务器加载整个其他JavaScript文件呢?
这是一个有点离题的答案,但是……您可能不需要使用网络工作者来处理浏览器上的长时间工作。
让我们假设你想要运行几次繁重的计算(就像你对数组做的那样):
const heavyFct = () => {let i = 0; while(i<1e8) {++i}}
for (let i = 0; i < 100; ++i) {
heavyFct();
}
这将冻结您的浏览器。
为了避免这种情况,我们可以这样依赖setTimeout:
const desync = (i = 0) => {
if (i >= 100) {return}
heavyFct();
setTimeout(() => desync(i + 1), 0);
}
desync();
现在,您可以在不冻结计算机的情况下运行繁重的计算
网络工作者在完全独立的上下文中操作,就像单个程序一样。
这意味着代码不能以对象的形式从一个上下文中移动到另一个上下文中,因为它们可以通过属于另一个上下文中的闭包引用对象。
这一点尤其重要,因为ECMAScript被设计成一种单线程语言,并且由于web worker在单独的线程中操作,那么您将有执行非线程安全操作的风险。
这再次意味着web工作者需要用源代码形式的代码进行初始化。
WHATWG的规格说明说
如果原点的结果
绝对URL是不一样的
输入脚本的起源,然后抛出
SECURITY_ERR异常。
因此,脚本必须是外部文件
和原来一样的方案
页面:您无法从
data: URL或javascript: URL,和一个
页面无法启动工作
使用http: url脚本。
但不幸的是,它并没有真正解释为什么不允许将带有源代码的字符串传递给构造函数。
@Trincot's似乎是目前为止最好的。然而,也许我们可以进一步发展它。所以我的想法是,
我们不要修改Function.prototype。
为线程操作获取函数的承诺/线程化版本。
如果需要,请确保仍然可以同步调用该函数。
因此,我们用spawn方法定义了Threadable类。一旦我们将函数设为该类的成员,那么它就是可线程的:)
class Threadable extends Function {
constructor(f){
super("...as",`return ${f.toString()}.apply(this,as)`);
}
spawn(...as){
var code = `self.onmessage = m => self.postMessage((${this.toString()}).apply(self,m.data));`,
blob = new Blob([code], {type: "text/javascript"}),
wrkr = new Worker(window.URL.createObjectURL(blob));
return new Promise( (v,x) => ( wrkr.onmessage = m => (v(m.data), wrkr.terminate())
, wrkr.onerror = e => (x(e.message), wrkr.terminate())
, wrkr.postMessage(as)
)
);
}
}
function add(...nums) {
return nums.reduce((a,b) => a+b);
}
var addT = new Threadable(add);
addT.spawn(1,2,3,4)
.then(m => console.log(`Promisified thread returned ${m}`));
console.log(`Synchronous invocation of addT returned ${addT(1,2,3,4)}`);
一个简单的承诺版本,Function#callAsWorker,它接受一个thisArg和参数(就像call一样),并返回一个承诺:
Function.prototype.callAsWorker = function (...args) {
return new Promise( (resolve, reject) => {
const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(...e.data));`,
blob = new Blob([code], { type: "text/javascript" }),
worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = e => (resolve(e.data), worker.terminate());
worker.onerror = e => (reject(e.message), worker.terminate());
worker.postMessage(args);
});
}
// Demo
function add(...nums) {
return nums.reduce( (a,b) => a+b );
}
// Let the worker execute the above function, with the specified arguments
add.callAsWorker(null, 1, 2, 3).then(function (result) {
console.log('result: ', result);
});