前端黑魔法: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獲得的字符串竟然是有做用的。信息差,也是會形成差距的。因此工程上也經驗和前瞻也一樣重要。

其餘參考資料

相關文章
相關標籤/搜索