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