![f2ff7dfd1efa4d9a92efe17782b3fcfa.png](https://i-blog.csdnimg.cn/blog_migrate/ec40c4aa232d1ce6f87e965378710864.jpeg)
前言
前几天,我和一位知乎网友讨论这个问题的时候,觉得这非常有意思,所以写了这篇文章作为记录
本文的思路和项目代码来源于知友 @simon3000,我加以修饰以更符合理解的需求。
本文所用代码已经得到当事人授权,请看:
![1d2dcd41e8d013d20aefa64eaf951b25.png](https://i-blog.csdnimg.cn/blog_migrate/1be23aa9e0e61008e7c0e2e749ffa695.jpeg)
非常感谢他的理解和鼓励, 然后还有一点要说明的是,他是从他使用worker-loader(webpack技术栈)的过程中得到灵感的。同时呢,他在实现makeworker函数功能的时候,除了学习了worker-loader的用法外,同时还非常巧妙的加入了整合Promise的用法,看起来真是让人赏心悦目。待会读一读便可知晓了。
作者初始代码地址(备注:GitHub是一个面向全世界开源的代码托管平台)
https://github.com/penghuwan/flex-webworker/blob/master/original-version/prototype.jsgithub.com![863d1440c42689e33b974efb5557cfc7.png](https://i-blog.csdnimg.cn/blog_migrate/76787c60d111d87ae910c3353c18febe.jpeg)
通过JS文件和路径创建webworker带来的问题
Webworker,我其实一直觉得用法比较生硬,因为似乎需要创建额外的JS文件才能运行,就像下面这样
var worker =new Worker('work.js’)
这意味着,你需要额外创建一个js文件。这种方式让我觉得有些“古板”。因为JS操纵文件的能力很差,如果想要创建文件,当然方法也有,参考:https://github.com/eligrey/FileSaver.js/
但是问题在于,如果想要创建文件,JS的文件创建往往离不开下载!我原本只是想“悄无声息”地创建一个文件,但结果JS在创建的时候突然弹出一个下载框,这可让人受不了。啊,难受。(此处应有[我太难了]表情包)。
也就是,这时候的webWorker是“静态”的,是需要额外JS文件的,是受约束的。
四次转换,将一个普通函数强行变成WebWorker
但是 @simon3000 的建议让我眼前一亮!他告诉我,根据他使用webworker-loader(webpack技术栈)的经验,有一种连续转换的方式可以直接将一个普通函数变成WebWorker
![7b5e5f75c6c1a087968706c777792f35.png](https://i-blog.csdnimg.cn/blog_migrate/7680655bf451266a427e326029f5bff8.jpeg)
这真是一个令人兴奋的信息。
试看看他的操作:
// 文件名为main.js
function work () {
onmessage = ({data: {message}}) => {
console.log ('i am worker, receive:' + message);
postMessage ({result: 'message from worker'});
};
}
const runWorker = f => {
const worker = new Worker (
URL.createObjectURL (new Blob ([`(${f.toString ()})()`]))
);
worker.onmessage = ({data: {result}}) => {
console.log ('i am main thread, receive:' + result);
};
worker.postMessage ({message: 'message from main thread'});
};
const testWorker = runWorker (work);
这段代码是我在他的代码基础上简化的
输出结果:
![89a510923932c00186b464bfdd0fd0d4.png](https://i-blog.csdnimg.cn/blog_migrate/5c918fef04c4f76bce8f6ece4d2fa01a.jpeg)
用Promise和闭包的方式去改造
我们再让它更通用一些,用Promise和闭包的方式去改造它,把runworker函数改造成一个makeworker函数
// 文件名为index.js
function work () {
onmessage = ({data: {jobId, message}}) => {
console.log ('i am worker, receive:-----' + message);
postMessage ({jobId, result: 'message from worker'});
};
}
const makeWorker = f => {
let pendingJobs = {};
const worker = new Worker (
URL.createObjectURL (new Blob ([`(${f.toString ()})()`]))
);
worker.onmessage = ({data: {result, jobId}}) => {
// 调用resolve,改变Promise状态
pendingJobs[jobId] (result);
// 删掉,防止key冲突
delete pendingJobs[jobId];
};
return (...message) =>
new Promise (resolve => {
const jobId = String (Math.random ());
pendingJobs[jobId] = resolve;
worker.postMessage ({jobId, message});
});
};
const testWorker = makeWorker (work);
testWorker ('message from main thread').then (message => {
console.log ('i am main thread, i receive:-----' + message);
});
输出结果
![622c959597f36f3f4d9a6fba491a1a46.png](https://i-blog.csdnimg.cn/blog_migrate/0a676cf821a5fe0f51a2a3e1078a5a97.jpeg)
总结
这次探讨告诉我们什么道理呢?
- 第一,function.toString得到的并不是一个没有意义的字符串,它是完全可以被用来运行的
- 第二,通过这种方式,webworker不需要借助额外的JS文件了,webworker完全动态化和自由化,你可以在主线程中创建任意个webworker!
- 第三,我通过这次的交谈了解到一个道理,编程除了考量逻辑思维,信息差也是考量的一大因素。我之前也想过用webworker做这些事情,可是我不知道能用这样的四重转换呀!我也不知道function.toString得到的字符串居然是有作用的。信息差,也是会造成差距的。所以工程上也经验和前瞻也同样重要。
其他参考资料
- worker-loader源码
![24bda6ab783f8dcf03b2a0b4da75abc8.png](https://i-blog.csdnimg.cn/blog_migrate/0639843b57dee255765bc9e184a0d10c.png)
(备注:GitHub是一个面向全世界开源的代码托管平台)