前端培訓-中級階段(24)-Web Workers 多線程(2019-11-07期)

前端最基礎的就是 HTML+CSS+Javascript。掌握了這三門技術就算入門,但也僅僅是入門,現在前端開發的定義已經遠遠不止這些。前端小課堂(HTML/CSS/JS),本着提升技術水平,打牢基礎知識的中心思想,我們開課啦(每週四)。

JS 是單線程事件循環模型。使用上來說如果有大量高強度的計算會導致 UI渲染進程卡頓。
按照 FPS: 60 來計算 1000ms/60 = 16.666ms。我們一段程序的佔用時間需要低於16.666ms

Web Workers 用法

new Worker(url),將會在 worker 線程中運行代碼,該環境與主線程不同

因爲並不是瀏覽器環境,所以 DOM、window 是無法使用的。當然也有一些API可以使用 localstroage、websocket、indexDB、XMLHttpRequest 等是沒問題的。

同時分爲兩種環境。

  1. 獨享模式:DedicatedWorkerGlobalScope (一個專用線程對應一個主線程)
  2. 共享模式:SharedWorkerGlobalScope(一個共享線程對應多個主線程)

主線程worker線程 之間使用 postMessage() 方法來發送信息,通過 onmessage 這個事件監聽來接收信息。

數據的傳輸方式爲傳遞副本,而不是直接共享數據。所以也導致有時傳遞的損耗高於計算的損耗。
當然,也支持整體內存移交給 worker,不過這樣使用的話,主線程就訪問不了這塊內容了。
測試地址

worker

獨享線程(Dedicated Web Worker),只能由創建時的主線程使用。

//html
wk = new Worker('/static/workers/1190000020913212.js');
demo2.addEventListener('click', function(){
    wk.postMessage(str)
})

//1190000020913212.js
onmessage = function(e) {
    var str = e.data;
    console.time('reportString');
    console.log(str.length, str.split('').join('+').length)
    console.timeEnd('reportString');
}

Shared Worker

同域多個窗口 多個腳本運行時,可以用於通信。
比如 iframe、標籤頁。

// html
swkPIP = new SharedWorker('/static/workers/1190000020913212-SharedWorker-pip.js');
swkPIP.port.start();
swkPIP.port.postMessage(args)

// worker.js
var portArr = [];
onconnect = function(e) {
    var port = e.ports[0];
    portArr.push(port)
    port.addEventListener('message', function(e) {
      console.log(self, e, port)
      if(e.data.type == 'private'){
          port.postMessage(['pip', e.data]);    
      }else if(e.data.type == 'public'){
          portArr.forEach(v=>v.postMessage(['pip', e.data]))
      }else if(e.data.type == 'publicNoSelf'){
          portArr.forEach(v=>v!=port&&v.postMessage(['pip', e.data]))
      }
    });
    port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter.
}

從實現代碼中可以看到。
Shared Worker 需要使用 prot 通道。還需要 start() 開啓通道。

可以實現的效果

  1. 跨窗口通信
  2. 比如說有一些輪詢的接口,在多窗口的場景中會形成密集的訪問。如果我們把請求轉入Shared Worker,Shared Worker中做節流。拉回數據再通知各個窗口。
  3. 比如用戶在一個窗口中登錄,把登錄狀態通知給其他窗口。

Service Workers

用於瀏覽器服務器之前的代理服務。可以實現離線訪問攔截請求更新緩存等。
navigator.serviceWorker.register('/static/js/sw-20190621.js')

其他事項

通過URL.createObjectURL()創建URL對象,可以實現創建內嵌的worker

開啓worker需要一個同源的URL。有時我們也不需要一個這樣的文件,那麼我們就可以通過一個指向內存的URL。

// worker.js
var str = `
    var i = 0;
    function test(){
        postMessage(++i);
        setTimeout(test, 1000);
    }
    test();
`;
// html
var blob = new Blob([str]);
var wk = new Worker(window.URL.createObjectURL(blob));

通過轉讓對象來傳遞數據

默認情況下,主線程與 worker 之前的數據傳遞是通過拷貝,也就是JSON.stringify()之後再發送,接受使用JSON.parse()處理。
這樣使用起來在會損耗一部分在序列化反序列化中。
所以 worker 給我們提供了一種更高效的方式,將整塊數據傳入(整塊內存移交,再源環境將不可訪問)。

myWorker.postMessage(uInt8Array, [uInt8Array]);

通過postMessage的第二個入參,我們把uInt8Array對象整個發送出去。

執行環境的上下文

我們都知道 瀏覽器環境的上下文Node環境的上下文是不一樣的,其中有一些是瀏覽器環境中獨有的。worker環境也是不一樣。
其中 self 指向當前環境的 global,與 Window 不是同一個對象,而是 WorkerGlobalScope

微信公衆號:前端linong

clipboard.png

參考文獻

  1. 前端培訓目錄、前端培訓規劃、前端培訓計劃
  2. https://www.sitepen.com/blog/the-return-of-sharedarraybuffers-and-atomics/
  3. JavaScript 性能利器 —— Web Worker
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章