js多線程解決方案Web?Worker簡單說明與實例演示
一、什么是Web Worker ?
JavaScript 語言是采用單線程模型,也就是任務(wù)只能在一個線程上完成,一次只能做一件事,前面任務(wù)沒執(zhí)行完,后面的任務(wù)只能排隊等待,由于多核 CPU 的出現(xiàn),單線程帶來很大不便,無法充分發(fā)揮計算機的能力。
Web Worker 就是為了 javascript 創(chuàng)造多線程而生的,主線程創(chuàng)建 worker 子線程,將一些任務(wù)分配給后臺運行,等到子線程完成計算任務(wù),再把結(jié)果返回給主線程,好處是計算密集型或高延遲的任務(wù)被 worker 負擔了,主線程就會很流暢。網(wǎng)頁加載展示可分為兩部分:主進程也叫 UI 進程,子進程也叫工作進程,子進程不能控制 UI 進程,只能進行數(shù)據(jù)交互。
Web Worker 子線程一旦創(chuàng)建成功,就會獨立于其他腳本始終運行,不會被主線程上活動打斷。這樣有利于隨時響應(yīng)主線程的通信。但是這也造成 Worker 比較耗費資源,不應(yīng)該過度使用,使用完畢之后應(yīng)該關(guān)閉。
使用 Web Worker 注意點:
- 同源限制:分配給 Worker 線程運行的腳本,必須與主線程的腳本文件同源,否則存在跨域問題。
- DOM限制:Worker 線程所在的全局對象,與主線程不同,無法讀取主線程的DOM對象,也無法使用 window、document、parent 這些對象。但是Worker線程可以使用navigation和location對象。
- 數(shù)據(jù)通信:Worker 線程與主線程不在一個環(huán)境,不能直接通信,必須通過消息來完成數(shù)據(jù)通信。
- 腳本限制:Worker 線程不能執(zhí)行 window 的 alert、confirm 方法。但是可以通過ajax發(fā)送請求。
- 文件限制:Worker線程無法讀取本地文件,子線程加載的腳本必須來自網(wǎng)絡(luò)。
相關(guān)API
1.Worker:構(gòu)造函數(shù),加載分線程執(zhí)行的js文件
2.Worker.prototype.onmessage:用于接受另一個線程的回調(diào)函數(shù)
3.WorKer.prototype.postMessage:向另一個線程發(fā)送消息
**不足**
worker內(nèi)代碼不能操作DOM
不能跨域加載JS
不是每個瀏覽器都支持這個新特性
var input = document.getElementById('number') function computed(n){ return n<=2 ? 1:computed(n-1) + computed(n-2) //遞歸調(diào)用 } document.getElementById('btn').onclick = function(){ var number = input.value var result = computed(number) alert(result) }
上面代碼當按鈕被點擊時根據(jù)用戶輸入的值進行斐波拉契數(shù)列的計算
當這個值較大時,由于遞歸導致頁面要很長時間才能響應(yīng),在等待響應(yīng)的過程中由于js的單線程機制導致我們不能進行任何操作,頁面就像被卡死了一樣,如果要解決這個問題,可以用 web Wokers實現(xiàn)
將計算的邏輯交給分線程執(zhí)行,這樣在計算的過程中我們可以正常操作頁面
//index.html var input = document.getElementById('number') document.getElementById('btn').onclick = function(){ var number = input.value //創(chuàng)建一個worker對象 var worker = new Worker('./js/worker.js') console.log(worker) //向分線程發(fā)送消息 worker.postMessage(number) console.log('主線程向分線程發(fā)送數(shù)據(jù):'+number) //綁定接受消息的監(jiān)聽 worker.onmessage = function(event){ console.log('主線程接受分線程返回的數(shù)據(jù):'+event.data) } }
//worker.js function computed(n){ return n<=2 ? 1:computed(n-1) + computed(n-2) //遞歸調(diào)用 } var onmessage = function(event){ var number = event.data console.log('分線程接受主線程發(fā)送的數(shù)據(jù):'+number) //計算 var result = computed(number) postMessage(result) console.log('分線程向主線程返回數(shù)據(jù):'+number) }
二、使用語法
2.1 創(chuàng)建Worker線程:
創(chuàng)建worker之前,先檢查瀏覽器是否支持它。使用 typeof 檢查,代碼如下:
if( typeof Worker !== undefined ){ console.log("支持Worker線程") }else{ console.log("不支持Worker") }
檢查瀏覽器支持 worker 之后,主線程使用 new 命令,調(diào)用 worker() 構(gòu)造函數(shù),新建 Worker 線程。
var myWorker = new Worker('worker.js')
構(gòu)造函數(shù)的參數(shù)是一個腳本文件,該文件不能是本地文件,必須來自網(wǎng)絡(luò)腳本,該文件就是Worker 線程要執(zhí)行的任務(wù)。如果該文件加載失敗,Worker 就會失敗。
2.2 主線程與子線程數(shù)據(jù)通信:
主線程調(diào)用 postMessage() 方法,向 Worker 發(fā)消息。postMessage(參數(shù)) 方法中參數(shù)就是傳給 Worker 的數(shù)據(jù),這個數(shù)據(jù)可以是任意格式。
myWorker.postMessage("你好")
緊接著 Worker 線程,通過 onmessage 指定監(jiān)聽函數(shù),接收消息。worker.js 代碼如下:
this.onmessage = function(res){ console.log("接收到消息",res.data) this.postMessage("我收到消息了") }
worker子進程收到消息之后,可以繼續(xù)向主進程發(fā)送消息,使用 postMessage()。代碼如上。
主進程也通過onmessage監(jiān)聽函數(shù)接收消息。
myWorker.onmessage = function(res){ console.log("主線程收到消息:",res.data) }
2.3 Worker線程
Worker線程內(nèi)部,添加 this.onmessage 監(jiān)聽函數(shù),其中 this 是子線程的全局對象,也可以替換成 self,self 代表子線程本身。等同于:
self.onmessage 《=》 this.onmessage
除了使用 self.onmessage 指定監(jiān)聽函數(shù),也可以使用 this.addEventListener() 監(jiān)聽事件對象。上述 worker.js 代碼可改為:
//寫法一 this.addEventListener("message",function(res){ console.log("res",res.data) }) //寫法二 addEventListener("message",function(res){ this.console.log("1",res.data) })
2.4 錯誤處理
主線程可以監(jiān)聽Worker是否發(fā)生錯誤,如果發(fā)生錯誤,Worker 會觸發(fā)主線程的 error 事件。
// 寫法一 myWorker.onerror = function(e){ console.log('e',e) } //寫法二 myWorker.addEventListener("error",function(e){ console.log("e",e) })
worker 子線程也可以監(jiān)聽 error 事件。
2.5 關(guān)閉 Worker
Worker 比較耗費資源,不應(yīng)該過度使用,使用完畢之后應(yīng)該關(guān)閉。主線程和子線程都可以關(guān)閉。
//主線程關(guān)閉 myWorker.terminate() //子線程關(guān)閉 self.close() //方法一 this.close() //方法二
三、同一個網(wǎng)頁的Web Worker
通常情況下,Worker 載入的是一個單獨的 javascript 文件,但是也可以載入與主線程在同一個網(wǎng)頁的代碼。網(wǎng)頁中添加 Worker 腳本,必須注意指定script標簽的type屬性是一個瀏覽器不認識的值,否則就會失去意義。如:
<script type="app/worker" id="wrs"> this.onmessage = function(res){ console.log("接收參數(shù)",res.data) } </script>
然后,需要讀取這段代碼,先將嵌入網(wǎng)頁的腳本代碼轉(zhuǎn)成二進制對象,然后為這個二進制對象生成url,再讓worker加載url,這樣就實現(xiàn)了主進程和worker在同一個網(wǎng)頁內(nèi)。代碼如下:
<script> var blob = new Blob([document.querySelector("#wrs").textContent]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url) worker.postMessage("發(fā)送數(shù)據(jù)") </script>
四、Worker 屬性和方法總結(jié)
Worker構(gòu)造函數(shù)方法:
- Worker.postMessage() - 發(fā)送消息。
- Worker.onmessage() - 監(jiān)聽子線程發(fā)送過來的數(shù)據(jù)。
- Worker.onmessageerror() - 發(fā)送數(shù)據(jù)無法序列化時觸發(fā)事件。
- Worker.onerror() - 錯誤處理。
- Worker.terminate() - 結(jié)束Worker。
子進程屬性方法:
Worker() 構(gòu)造函數(shù),可以接受兩個參數(shù),第一個是腳本的地址,第二個是參數(shù)是配置對象,該對象指定Worker的名稱。如:
var myWorker = new Worker('worker.js', { name : 'myWorker' });
- self.name - Worker 的名字
- self.onmessage - 接收消息
- self.postmessage - 發(fā)送數(shù)據(jù)
- self.onerror - 錯誤處理
- self.onmessageerror - 發(fā)送的數(shù)據(jù)無法序列化成字符串時觸發(fā)事件
- self.close - 關(guān)閉Worker線程
- self.importScript() - 加載js腳本
以上就是js多線程解決方案Web Workers簡單說明與實例演示的詳細內(nèi)容,更多關(guān)于js多線程解決方案Web Workers說明的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
2019年度web前端面試題總結(jié)(主要為Vue面試題)
轉(zhuǎn)眼2019又要過去了,作為一名前端碼農(nóng),又熬過一個沒日沒夜的年頭,頭發(fā)又少了不少,去年的學習計劃一半也沒完成,唉。。。 現(xiàn)在為大家總結(jié)一下這一年面試的幾家公司的關(guān)于前端面試題吧2020-01-01