Web Workers

JavaScript 通常在作業系統的 Main Thread 執行,但若把程式碼放在 Web Workers 就可另闢戰場-Worker Thread,兩條線互不影響,讓 JavaScript 在背景執行,並且兩線可由訊息溝通-使用 postMessage 發送訊息、onmessage 接收訊息。通常我們會將需要長時間運算且不含 Window 或 DOM Element 操作的程式碼放在 Web Workers,好處是不阻塞 Main Thread 而讓速度變快。

Web Workers

圖片來源:Browser Rendering Optimization - JavaScript

瀏覽器支援度

主流瀏覽器皆支援。

Web Workers 瀏覽器支援度

圖片來源:Can I use…Web Workers

注意,Shared Web Workers 目前只能用於 Chrome 66+、Firefox 60+,且手機幾乎不支援。

Shared Web Workers

圖片來源:Can I use…Shared Web Workers

範例

從範例理解 Web Worker 到底是什麼和怎麼用吧 (๑•̀ㅂ•́)و✧

由於 Worker 是需要起 Server 的,推薦使用 Web Server for Chrome 來執行範例網站。

兩數相乘(Dedicated Web Workers)

這裡簡單使用 Dedicated Web Workers 來做相乘運算,算完後再丟回 Main Thread。

兩數相乘範例

說明如下

以下是主程式的部分程式碼。

if (window.Worker) {
  var myWorker = new Worker('worker.js');

  first.onchange = function () {
    myWorker.postMessage([first.value, second.value]);
    console.log('Message posted to worker');
  };

  second.onchange = function () {
    myWorker.postMessage([first.value, second.value]);
    console.log('Message posted to worker');
  };

  myWorker.onmessage = function (e) {
    result.textContent = e.data;
    console.log('Message received from worker');
  };
}

以下是 Web Workers 的部分程式碼,Web Workers 使用 onmessage 監聽 Main Thread 是否傳送資料,收到資料後即在 Worker Thread 進行運算,算完再用 postMessage 回應給 Main Thread(註一)。

onmessage = function (e) {
  console.log('Message received from main script');
  var workerResult = 'Result: ' + e.data[0] * e.data[1];
  console.log('Posting message back to main script');
  postMessage(workerResult);
};

注意

兩數相乘(Shared Web Workers)

Shared Web Workers 能夠被多個程式腳本存取,即使是跨越不同 window、iframe 或 worker。

這個範例依然是兩數相乘,只是分別演示除了兩數相乘外,還可以計算平方,但皆用同一個 Worker 做運算。

這裡有兩個腳本 square.jsmultiply.js,它們都共用 worker.js 來做兩數相乘的運算。

使用 multiply.js 的畫面。

兩數相乘範例

使用 square.js 的畫面。

兩數相乘範例

和 Dedicated Workers 做法類似(如上例),除了以下三個部份…

一、用 SharedWorker 建構子來產生 Shared Workers。

以下是主程式的部分程式碼。

if (!!window.SharedWorker) {
  var myWorker = new SharedWorker('worker.js');

  // ...
}

二、與 Shared Worker 的溝通必須要透過 port 物件(註一),其實 Dedicated Workers 也是如此,只不過一切是在背景自動完成。

以下是主程式的部分程式碼。

squareNumber.onchange = function () {
  myWorker.port.postMessage([squareNumber.value, squareNumber.value]);
  console.log('Message posted to worker');
};

以下是 Web Workers 的部分程式碼。

onconnect = function(e) { // 監聽連線建立的 onconnect 事件,從 onconnect 取得 port 物件
  var port = e.ports[0];

  port.onmessage = function(e) {
    var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
    port.postMessage(workerResult);
  }
}

三、腳本可共用 Shared Workers 來做運算。例如,square.jsmultiply.js 共用 worker.js 來做兩數相乘的運算。

Image Manipulator

這裡有一個 Image Manipulator,由於圖片處理是很花效能的,因此希望將這部分的計算移到 Web Workers。

下圖是 Demo,這個 Image Manipulator 在上傳圖片後,可分別做「Invert」、「Chroma」、「Greyscale」、「Vibrant」和「回到原圖」,前四個功能在處理什麼其實我不太清楚,反正就是對圖做些事情摟 XD

Timer & Image Manipulator Demo

這裡是改善前的原始碼,而改善後的解答在此

改善方式是將這一段計算放到 worker.js 中,並在 worker.js 引入計算所用到的函式庫,待計算完再丟回 Main Thread,然後顯示結果。

改善前。

function manipulateImage(type) {
    var a, b, g, i, imageData, j, length, pixel, r, ref;
    imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

    toggleButtonsAbledness();

    // 複雜計算 ... 省略程式碼

    toggleButtonsAbledness();
    return ctx.putImageData(imageData, 0, 0);
  };

改善後,複雜計算移到 Worker Thread,算完後丟回 Main Thread。

以下是主程式的部分程式碼。

var imageWorker = new Worker('scripts/worker.js');

function manipulateImage(type) {
  var a, b, g, i, imageData, j, length, pixel, r, ref;
  imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  toggleButtonsAbledness();
  imageWorker.postMessage({'imageData': imageData, 'type': type});

  imageWorker.onmessage = function(e) {
    toggleButtonsAbledness();
    var image = e.data;
    if (image) return ctx.putImageData(image, 0, 0);
  }

  // 略...
};

以下是 Web Workers 的部分程式碼。

importScripts('imageManips-improved.js');

this.onmessage = function(e) {
  var imageData = e.data.imageData;
  var type = e.data.type;

  try {
      // 複雜計算 ... 省略程式碼
    }
    postMessage(imageData);
  } catch (e) {
    function ManipulationException(message) {
      this.name = "ManipulationException";
      this.message = message;
    };
    throw new ManipulationException('Image manipulation error');
    postMessage(undefined);
  }
}

效能比較

改用 Web Workers 後,效能改進多少呢?看 Invert 所花的時間。

改善前 Invert 所花的時間

改善前 Invert 所花的時間: manipulateImage

改善後 Invert 所花的時間

改善後 Invert 所花的時間: manipulateImage

改善後 Invert 所花的時間: Workers

不阻塞 Main Thread 且變快了 d(`・∀・)b

More

若想再玩玩可以看這個範例-qrcode,分支 solution 即是解答。

關於 Worker 的說明。

備註

參考資料


Web Workers 效能調校 轉譯效能 Rendering Performance Chrome DevTools javascript Worker 前端效能 系列文