前端黑魔法:webworker动态化,无需JS文件建立worker

前言

前几天,我和一位知乎网友讨论这个问题的时候,以为这很是有意思,因此写了这篇文章做为记录
本文的思路和项目代码来源于知友 @simon3000,我加以修饰以更符合理解的需求。
 
本文所用代码已经获得当事人受权,请看:
很是感谢他的理解和鼓励
 

做者初始代码地址

 (进入项目页面,里面的original-version目录下就是做者的最初的代码)webpack

 


经过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技术栈)的经验,有一种连续转换的方式能够直接将一个普通函数变成WebWorkergit

 
这真是一个使人兴奋的信息。
 
试看看他的操做:
// 文件名为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);
这段代码是我在他的代码基础上简化的
 
输出结果:
 

用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);
});

 

输出结果
 

总结

此次探讨告诉咱们什么道理呢?
  • 第一,function.toString获得的并非一个没有意义的字符串,它是彻底能够被用来运行的
  • 第二,经过这种方式,webworker不须要借助额外的JS文件了,webworker彻底动态化和自由化,你能够在主线程中建立任意个webworker!
  • 第三,我经过此次的交谈了解到一个道理,编程除了考量逻辑思惟,信息差也是考量的一大因素。我以前也想过用webworker作这些事情,但是我不知道能用这样的四重转换呀!我也不知道function.toString获得的字符串竟然是有做用的。信息差,也是会形成差距的。因此工程上也经验和前瞻也一样重要。

其余参考资料

相关文章
相关标签/搜索