摘要:不過,這并不意味著語言本身就支持了多線程,對于語言本身它仍是運行在單線程上的,只是瀏覽器宿主環境提供的一個能力。主線程與子線程之間也可以交換二進制數據,比如等對象,也可以在線程之間發送。
先看幾個例子
本例子是通過通過紅點展示地球上的地震帶,數據來自于地質探測局
通過console.log看到數據運算所耗的時間
不使用 webworker No web workers - all on main thread
使用一條 webworker One web worker
使用兩條 Two web workers
使用八條 Eight web workers
使用20條 20 web workers
結論:是? // 帶著思考看下去
背景JavaScript引擎是單線程運行的,JavaScript中耗時的I/O操作都被處理為異步操作,它們包括鍵盤、鼠標I/O輸入輸出事件、窗口大小的resize事件、定時器(setTimeout、setInterval)事件、Ajax請求網絡I/O回調等。當這些異步任務發生的時候,它們將會被放入瀏覽器的事件任務隊列中去,等到JavaScript運行時執行線程空閑時候才會按照隊列先進先出的原則被一一執行,但終究還是單線程。
雖然JS運行在瀏覽器中,是單線程的,每個window一個JS線程,但瀏覽器不是單線程的,例如Webkit或是Gecko引擎,都可能有如下線程:
javascript引擎線程 界面渲染線程 瀏覽器事件觸發線程 Http請求線程
很多人覺得異步(promise async/await),都是通過類似event loop在平常的工作中已經足夠,但是如果做復雜運算,這些異步偽線程的不足就逐漸體現出來,比如settimeout拿到的值并不正確,再者假如頁面有復雜運算的時候頁面很容易觸發假死狀態,
為了有多線程功能,webworker問世了。不過,這并不意味著 JavaScript 語言本身就支持了多線程,對于 JavaScript 語言本身它仍是運行在單線程上的, Web Worker 只是瀏覽器(宿主環境)提供的一個能力/API。
Web Worker 是HTML5標準的一部分,這一規范定義了一套 API,它允許一段JavaScript程序運行在主線程之外的另外一個線程中。工作線程允許開發人員編寫能夠長時間運行而不被用戶所中斷的后臺程序, 去執行事務或者邏輯,并同時保證頁面對用戶的及時響應,可以將一些大量計算的代碼交給web worker運行而不凍結用戶界面,后面會有案例介紹
類型Web workers可分為兩種類型:專用線程dedicated web worker,以及共享線程shared web worker。 Dedicated web worker隨當前頁面的關閉而結束;這意味著Dedicated web worker只能被創建它的頁面訪問。與之相對應的Shared web worker可以被多個頁面訪問。在Javascript代碼中,“Work”類型代表Dedicated web worker,而“SharedWorker”類型代表Shared web worker。
而Shared Worker則可以被多個頁面所共享(同域情況下)
Web Worker的創建是在主線程當中通過傳入文件的url來實現的。如下所示:
let webworker = new Worker("myworker.js");
返回的是webworker實例對象,該對象是主線程和其他線程的通訊橋梁
主線程和其他線程可以通過
onmessage: 監聽事件 postmessage: 傳送事件
相關的API進行通訊
案例代碼如下
//主線程 main.js var worker = new Worker("worker.js"); worker.onmessage = function(event){ // 主線程收到子線程的消息 }; // 主線程向子線程發送消息 worker.postMessage({ type: "start", value: 12345 }); //web worker.js onmessage = function(event){ // 收到 }; postMessage({ type: "debug", message: "Starting processing..." });
相關demo
如何終止如果在某個時機不想要 Worker 繼續運行了,那么我們需要終止掉這個線程,可以調用 在主線程worker 的 terminate 方法 或者在相應的線程中調用close:
// 方式一 main.js 在主線程停止方式 var worker = new Worker("./worker.js"); ... worker.terminate(); // 方式二、worker.js self.close()錯誤機制
提供了onerror API
worker.addEventListener("error", function (e) { console.log("MAIN: ", "ERROR", e); console.log("filename:" + e.filename + "-message:" + e.message + "-lineno:" + e.lineno); }); // event.filename: 導致錯誤的 Worker 腳本的名稱; // event.message: 錯誤的信息; // event.lineno: 出現錯誤的行號;sharedWorker
對于 Web Worker ,一個 tab 頁面只能對應一個 Worker 線程,是相互獨立的;
而 SharedWorker 提供了能力能夠讓不同標簽中頁面共享的同一個 Worker 腳本線程;
當然,有個很重要的限制就是它們需要滿足同源策略,也就是需要在同域下;
在頁面(可以多個)中實例化 Worker 線程:
// main.js var myWorker = new SharedWorker("worker.js"); myWorker.port.start(); myWorker.port.postMessage("hello, I"m main"); myWorker.port.onmessage = function(e) { console.log("Message received from worker"); }
// worker.js onconnect = function(e) { var port = e.ports[0]; port.addEventListener("message", function(e) { var workerResult = "Result: " + (e.data[0]); port.postMessage(workerResult); }); port.start(); }
在線demo
線程中再創建線程
環境與作用域在 Worker 線程的運行環境中沒有 window 全局對象,也無法訪問 DOM 對象,所以一般來說他只能來執行純 JavaScript 的計算操作。但是,他還是可以獲取到部分瀏覽器提供的 API 的:
setTimeout(), clearTimeout(), setInterval(), clearInterval():有了設計個函數,就可以在 Worker : 線程中可以再創建worker;
XMLHttpRequest : 對象:意味著我們可以在 Worker 線程中執行 ajax 請求;
navigator 對象:可以獲取到 ppName,appVersion,platform,userAgent 等信息;
location 對象(只讀):可以獲取到有關當前 URL 的信息;
Application Cache
indexedDB
WebSocket、
Promise、
在線程中,提供了importScripts方法
如果線程中使用了importScripts 一般按照以下步驟解析
1、解析 importScripts方法的每一個參數。 2、如果有任何失敗或者錯誤,拋出 SYNTAX_ERR 異常。 3、嘗試從用戶提供的 URL 資源位置處獲取腳本資源。 4、對于 importScripts 方法的每一個參數,按照用戶的提供順序,獲取腳本資源后繼續進行其它操作。
// worker.js importScripts("math_utilities.js"); onmessage = function (event) { var first=event.data.first; var second=event.data.second; calculate(first,second); // calculate 是math_utilities.js中的方法 };
也可以一次性引入多個
//可以多起一次傳入 importScripts("script1.js", "script2.js");XMLHttpRequest
onmessage = function(evt){ var xhr = new XMLHttpRequest(); xhr.open("GET", "serviceUrl"); //serviceUrl為后端j返回son數據的接口 xhr.onload = function(){ postMessage(xhr.responseText); }; xhr.send(); }
// 設置jsonp function MakeServerRequest() { importScripts("http://SomeServer.com?jsonp=HandleRequest"); } // jsonp回調 function HandleRequest(objJSON) { postMessage("Data returned from the server...FirstName: " + objJSON.FirstName + " LastName: " + objJSON.LastName); } // Trigger the server request for the JSONP data MakeServerRequest();通訊原理
從一個線程到另一個線程的通訊實際上是一個值拷貝的過程,實際上是先將數據JSON.stringify之后再JSON.parse。主線程與子線程之間也可以交換二進制數據,比如File、Blob、ArrayBuffer等對象,也可以在線程之間發送。但是,用拷貝方式發送二進制數據,會造成性能問題。比如,主線程向子線程發送一個50MB文件,默認情況下瀏覽器會生成一個原文件的拷貝。為了解決這個問題,JavaScript允許主線程把二進制數據直接轉移給子線程,轉移后主線程無法再使用這些數據,這是為了防止出現多個線程同時修改數據的問題,這種轉移數據的方法,叫做Transferable Objects。
不過現在很多瀏覽器支持transferable objects(可轉讓對象) ,這個技術是零拷貝轉移,能大大提升性能,
可以指定傳送的數據全都是零拷貝
var abBuffer = new ArrayBuffer(32); aDedicatedWorker.postMessage(abBuffer, [abBuffer]);
也可以 指定某個是 使用 零拷貝
var objData = { "employeeId": 103, "name": "Sam Smith", "dateHired": new Date(2006, 11, 15), "abBuffer": new ArrayBuffer(32) }; aDedicatedWorker.postMessage(objData, [objData.abBuffer]);工作線程生命周期
工作線程之間的通信必須依賴于瀏覽器的上下文環境,并且通過它們的 MessagePort 對象實例傳遞消息。每個工作線程的全局作用域都擁有這些線程的端口列表,這些列表包括了所有線程使用到的 MessagePort 對象。在專用線程的情況下,這個列表還會包含隱式的 MessagePort 對象。
每個工作線程的全局作用域對象 WorkerGlobalScope 還會有一個工作線程的線程列表,在初始化時這個列表為空。當工作線程被創建的時候或者擁有父工作線程的時候,它們就會被填充進來。
最后,每個工作線程的全局作用域對象 WorkerGlobalScope 還擁有這個線程的文檔模型,在初始化時這個列表為空。當工作線程被創建的時候,文檔對象就會被填充進來。無論何時當一個文檔對象被丟棄的時候,它就要從這個文檔對象列舉里面刪除出來。
// 部分機器webwoker初始化時間 Macbook Pro: 2 workers, 0.4 milliseconds on average Macbook Pro: 4 workers, 0.6 milliseconds on average Nexus 5: 2 workers, 6 milliseconds on average Nexus 5: 4 workers, 15 milliseconds on average (border-line UI jank)
1、普通json/object
2、tranferable objects
可見 transferable objects傳輸速度要高很多
1) 使用專用線程進行數學運算
Web Worker最簡單的應用就是用來做后臺計算,而這種計算并不會中斷前臺用戶的操作
2) 圖像處理
通過使用從或者元素中獲取的數據,可以把圖像分割成幾個不同的區域并且把它們推送給并行的不同Workers來做計算
3) 大量數據的檢索
當需要在調用 ajax后處理大量的數據,如果處理這些數據所需的時間長短非常重要,可以在Web Worker中來做這些,避免凍結UI線程。
4) 背景數據分析
由于在使用Web Worker的時候,我們有更多潛在的CPU可用時間,我們現在可以考慮一下JavaScript中的新應用場景。例如,我們可以想像在不影響UI體驗的情況下實時處理用戶輸入。利用這樣一種可能,我們可以想像一個像Word(Office Web Apps 套裝)一樣的應用:當用戶打字時后臺在詞典中進行查找,幫助用戶自動糾錯等等。
1、不能訪問DOM和BOM對象的,Location和navigator的只讀訪問,并且navigator封裝成了WorkerNavigator對象,更改部分屬性。無法讀取本地文件系統
2、子線程和父級線程的通訊是通過值拷貝,子線程對通信內容的修改,不會影響到主線程。在通訊過程中值過大也會影響到性能(解決這個問題可以用transferable objects)
3、并非真的多線程,多線程是因為瀏覽器的功能
4、兼容性
5 因為線程是通過importScripts引入外部的js,并且直接執行,其實是不安全的,很容易被外部注入一些惡意代碼
6、條數限制,大多瀏覽器能創建webworker線程的條數是有限制的,雖然可以手動去拓展,但是如果不設置的話,基本上都在20條以內,每條線程大概5M左右,需要手動關掉一些不用的線程才能夠創建新的線程(相關解決方案)
7、js存在真的線程的東西,比如SharedArrayBuffer
1、tagg2
參考文獻:[1] https://www.html5rocks.com/zh...
[2] http://www.alloyteam.com/2015...
[3] https://typedarray.org/concur...
[4] http://www.andygup.net/advanc...
[5] https://developer.mozilla.org...
[6] http://coolaj86.github.io/htm...
[7] http://www.xyhtml5.com/webwor...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91691.html
摘要:淺談前言都知道是單線程語言,最讓人頭疼的莫過于在網絡正常的情況下經常出現頁面的假死,以及在進行大量的循環計算時會導致線程阻塞由于要進行大量的計算后面的運行會被阻隔在此處,使得性能較差,代碼維護性差等一系列的問題發生。 WebWork淺談 前言: 都知道JS是單線程語言,最讓人頭疼的莫過于在網絡正常的情況下經常出現頁面的假死, 以及在進行大量的for循環計算時會導致線程阻塞,由于要進行...
摘要:淺談前言都知道是單線程語言,最讓人頭疼的莫過于在網絡正常的情況下經常出現頁面的假死,以及在進行大量的循環計算時會導致線程阻塞由于要進行大量的計算后面的運行會被阻隔在此處,使得性能較差,代碼維護性差等一系列的問題發生。 WebWork淺談 前言: 都知道JS是單線程語言,最讓人頭疼的莫過于在網絡正常的情況下經常出現頁面的假死, 以及在進行大量的for循環計算時會導致線程阻塞,由于要進行...
摘要:淺談前言都知道是單線程語言,最讓人頭疼的莫過于在網絡正常的情況下經常出現頁面的假死,以及在進行大量的循環計算時會導致線程阻塞由于要進行大量的計算后面的運行會被阻隔在此處,使得性能較差,代碼維護性差等一系列的問題發生。 WebWork淺談 前言: 都知道JS是單線程語言,最讓人頭疼的莫過于在網絡正常的情況下經常出現頁面的假死, 以及在進行大量的for循環計算時會導致線程阻塞,由于要進行...
閱讀 3209·2021-11-24 09:39
閱讀 2939·2021-11-23 09:51
閱讀 898·2021-11-18 10:07
閱讀 3548·2021-10-11 10:57
閱讀 2749·2021-10-08 10:04
閱讀 3007·2021-09-26 10:11
閱讀 1052·2021-09-23 11:21
閱讀 2792·2019-08-29 17:28