zoukankan      html  css  js  c++  java
  • 前端黑魔法:webworker动态化,无需JS文件创建worker

    前言

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

    作者初始代码地址

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


    通过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

     
    这真是一个令人兴奋的信息。
     
    试看看他的操作:
    // 文件名为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得到的字符串居然是有作用的。信息差,也是会造成差距的。所以工程上也经验和前瞻也同样重要。

    其他参考资料

  • 相关阅读:
    Dom修改元素样式
    URL百分号编码
    accesskey附上一些实例
    dom实例
    dom 创建时间
    关系运算符
    赋值运算符
    js图片随机切换
    js自增图片切换
    transform-origin盒子旋转位置
  • 原文地址:https://www.cnblogs.com/penghuwan/p/11546407.html
Copyright © 2011-2022 走看看