web worker 掃盲篇


什么是woker

官方的解釋是這樣的:

worker是一個對象,通過構造函數Worker創建,參數就是一個js文件的路徑;文件中的js代碼將運行在主線程之外的worker線程;

var jsFileURI = JS_FILE_PATH;  // js文件路徑

var worker = new Worker(jsFileURI);

worker運行在另一個全局上下文中(self),這個全局上下文不同於window,所以不能在woker中訪問window和DOM;

該線程分為兩種:dedicated workershared worker;dedicated worker只能被初始化它的js上下文中使用;shared worker可以在多個js上下文中使用。通常使用的worker是dedicated worker,它的工作情況可以通過chrome的調試工具查看。

 

為什么引入woker?

前端開發者應該知道瀏覽器中JS和UI公用一個線程,JS計算過程中,不能響應UI;如果遇到計算量比較大的任務,如操作圖像像素時,會造成用戶行為得不到響應。Web Worker 是為了解決 JavaScript 在瀏覽器環境中沒有多線程的問題。支持 Web Worker 的瀏覽器會額外提供一個 JavaScript Runtime 供 Web Worker 使用。它的最佳使用場景是執行一些開銷較大的數據處理或計算任務。

 

woker是怎么工作的?

Web Worker 使用起來非常簡單,在“主線程”中執行如下操作即可創建一個 Worker 實例,通過監聽 onmessage 事件獲取消息,通過 postMessage 發送消息:

“主線程”和Worker 之間通過 postMessage 發送消息,通過監聽 onmessage 事件來接收消息,從而實現二者的通信。

如下圖所示:

核心代碼如下:

主線程中代碼

var worker = new Worker('worker.js');
worker.onmessage = function (e) {
    var data = e.data;
}
var messageData = {
    message: 'hello worker!'
};
worker.postMessage(messageData);

worker.js 中的代碼如下:

self.onmessage = function(e) {
    var messages = e.data;  // e.data為{message: 'hello worker!'}
    var workerResult = {};
    // do something
    ...
    postMessage(workerResult);
}
 

使用woker的幾個tips

(1)使用多少個worker?

遇到復雜的計算,需要開啟多少worker才合適呢?一般的做法是參考navigator.hardwareConcurrency 這個屬性,它表示機器支持的並行最大任務數。還有一種動態檢測 Worker 數量的方法,有興趣的話可以看:https://github.com/oftn-oswg/core-estimator。

(2)優化woker與主線程通信開銷

該段主要參考百度地圖技術博客(https://mp.weixin.qq.com/mp/getmasssendmsg?__biz=MzIzNDE0NjMzOQ==#wechat_webview_type=1&wechat_redirect)。

Worker 與“主線程”之間的數據傳遞默認是通過結構化克隆(Structured Clone)完成的。數據量較大時,克隆過程會比較耗時,這會影響 postMessage 和 onmessage 函數的執行時間。

解決的辦法一是先通過 JSON.stringify 將對象序列化,接收之后再用 JSON.parse 還原。因為:stringfiy + 傳遞字符串的耗時 < 傳遞對象的耗時 。

代碼如下:

// 操作像素
    var imageData = context.createImageData(img.width, img.height);
    var work = new Worker('./cal.js');
    var data = {
        data: imageData.data,
        width: imageData.width,
        height: imageData.height
    };
    // 將傳遞的參數轉換成字符串
    work.postMessage(JSON.stringify(data));

 

還有一種避開克隆傳值的方法,就是使用Transferable Objects,主要是采用二進制的存儲方式,采用地址引用,解決數據交換的實時性問題;Transferable Objects支持的常用數據類型有ArrayBuffer和ImageBitmap;

使用方法如下:

   // 操作像素
    var imageData = context.createImageData(img.width, img.height);
    var work = new Worker('./cal.js');
    // 轉化為類型數組進行傳遞
    var int8s = new Int8Array(imageData.data);
    var data = {
        data: int8s,
        width: imageData.width,
        height: imageData.height
    };
    // 在postMessage方法的第二個參數中指定transferList
    work.postMessage(data, [data.data.buffer]);

經測試,使用arrayBuffer之后,傳遞數據所需的時間為1ms,極大地提高了數據傳輸的效率。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM