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