PWA(Progressive Web App)入门系列:(五)Web Worker
前言
在說Service Worker前有必要說一下Web Worker,因為Service Worker本身就屬于Web Worker的延伸,大部分功能也是基于Web Worker進行的擴展。
背景
眾所周知,JavaScript引擎是以單線程調(diào)度的方式進行,我們無法同時運行多個JavaScript文件,這種情況下就會導致對硬件資源無法充分利用,并且當在進行一些高耗性能的操作時,會影響主線程的其他任務,造成任務阻塞及用戶體驗差等問題。
在這種劣勢情況下,到 2008 年 W3C 提出第一個 HTML5 草案開始,就在 HTML5 中提出了Web Worker的概念,并規(guī)范了Web Worker的三大特征:
- 能夠長時間運行
- 理想的啟動性能
- 理想的內(nèi)存消耗
簡介
Web Worker 是HTML5標準的一部分,這一規(guī)范定義了一套 API。實現(xiàn)了 用Web Worker 來實現(xiàn) JavaScript 的 “多線程” 技術,并發(fā)執(zhí)行多個 JavaScript 腳本。
Web Worker 與傳統(tǒng)多線程
每個JavaScript腳本執(zhí)行流都稱為一個線程,彼此之間互相獨立,并且有瀏覽器中的 JavaScript 引擎負責管理,當然這并不是說JavaScript支持多線程,雖然傳統(tǒng)JavaSript有多種方式實現(xiàn)了對多線程的模擬(例如:setinterval,setTimeout,以及一些異步的操作方法等),但是在本質(zhì)上程序的運行仍然是由 JavaScript 引擎以單線程調(diào)度的方式運行的,而Web Worker的線程是依賴于瀏覽器(宿主環(huán)境)來實現(xiàn)的,從而實現(xiàn)了對瀏覽器端多線程編程的支持。
Web Worker 線程種類
Web Worker 有兩種不同線程類型,分別是:
- Dedicated Worker (專用線程)。只能被首次生成它的腳本使用
- Shared Worker (共享線程)。可以同時被多個腳本使用
通常來說的Web Worker指的就是Dedicated Worker,Service Worker也屬于其中,并且各大瀏覽器對其支持良好,而Shared Worker指的是SharedWorker,目前各大瀏覽器對其支持度較差。
這里主要對Dedicated Worker進行詳細說明,對于Shared Worker不再進行細說。
Worker模式
Worker線程執(zhí)行流
創(chuàng)建 Web Worker
下面說一下如何創(chuàng)建一個Web Worker
語法:
new Worker(in DOMString aStringURL );使用上面的方式即可以創(chuàng)建一個Web Worker對象,它執(zhí)行的是aStringURL中的腳本。目前大多數(shù)瀏覽器是支持data URI的aStringURL的,可以通過URL.createObjectURL(blob)創(chuàng)建。但需要注意的是腳本必須遵循同源策略。
下面創(chuàng)建一個Worker,Worker的執(zhí)行腳本是workerfile.js,創(chuàng)建成功后,它會返回一個新的Worker對象賦值給前面聲明的workerObj變量
var workerObj = new Worker('./workerfile.js');這里需要注意, worker線程的創(chuàng)建的是異步的,主線程代碼不會阻塞在這里去等待worker線程去加載、執(zhí)行相應的腳本文件,而是會立即向下執(zhí)行后面代碼。
Web Worker實例方法
Worker的實例方法只有兩個:
- postMessage
- terminate
postMessage
主線程向生成的Worker線程發(fā)送數(shù)據(jù)的方法。
語法:
workerObj.postMessage(aMessage, transferList);- aMessage:向Worker線程發(fā)送的消息數(shù)據(jù)對象。它可以是任何類型的值或JavaScript對象。
- transferList:可選。Transferable類型的數(shù)組。主要用在 ArrayBuffer, MessagePort, ImageBitmap對象。
注意:
postMessage發(fā)送的aMessage參數(shù),在傳遞通訊的時候會對數(shù)據(jù)進行克隆,為了防止多個線程間的數(shù)據(jù)同時修改的問題。實際上,瀏覽器內(nèi)部的實現(xiàn)是,先將通信傳遞的數(shù)據(jù)串行化,隨后把串行化后的數(shù)據(jù)發(fā)給子線程,后者再將數(shù)據(jù)還原。
postMessage也可以以二進制的方式傳輸,例如 ArrayBuffer 、File、Blob、ImageBitmap等對象。但是往往傳輸?shù)倪@些對象數(shù)據(jù)量都很大,前面說了傳輸數(shù)據(jù)會進行拷貝,如果傳一個100MB的數(shù)據(jù),那么瀏覽器默認會再復制一份100MB的數(shù)據(jù),導致一些不必要的資源消耗。為了防止這種問題,就可以使用上面說的第二個參數(shù)transferList來解決。
順道科普一下Transferable接口。這個接口代表一個能在不同可執(zhí)行上下文中相互傳遞的對象,例如主線程和Worker線程。
var arrBuff = new ArrayBuffer(8); myWorker.postMessage(arrBuff, [arrBuff]);terminate
語法:
workerObj.terminate()用于立即終止worker對象的行為,如果worker正在運行著任務也會立即終止。
Web Worker實例屬性
Worker實例包含兩個屬性:
- onmessage:用來接收worker線程傳遞過來的數(shù)據(jù)事件。
- onerror:用來接收worker線程的錯誤信息。
onmessage
onmessage屬性表示一個EventHandler事件處理函數(shù),當Worker子線程返回一條消息時被調(diào)用。
語法:
workerObj.onmessage = function(e) { ... }傳遞來的消息被封裝在事件的data屬性中。
workerObj.onmessage = function(e) {var result = e.data; }onerror
onerror屬性是EventListener 一個事件監(jiān)聽函數(shù),一旦有類型為 error 的 ErrorEvent 從 worker線程中冒泡出來時就會執(zhí)行該函數(shù)。可以通過preventDefault()來取消冒泡。
主要用到的錯誤屬性有:
- message: 可讀的錯誤信息
- filename: 發(fā)生錯誤的腳本文件名稱
- lineno: 發(fā)生錯誤的腳本所在文件的行數(shù)
Web Worker文件方法
Worker線程對象抽象于DedicatedWorkerGlobalScope接口。此作用域下沒有window對象,需要用self來調(diào)用。
在發(fā)送數(shù)據(jù)和接收數(shù)據(jù)使用的方法和worker實例對象的一樣:
- postMessage
- onerror
這兩個方法就不說了,還有一個close方法說一下。
close
這個和terminate()有點類似。這個方法主要用來清除所有在WorkerGlobalScope事件環(huán)中的排隊任務,關閉特定作用域。
self.close()importScripts 導入腳本
WorkerGlobalScope 對象中可以使用importScripts()方法來進行對腳本文件和資源的引入。
但這個操作需要注意:
- 如果沒有給 importScripts 方法任何參數(shù),那么立即返回,終止下面的步驟。
- 解析 importScripts 方法的每一個參數(shù)。
- 如果有任何失敗或者錯誤,拋出 SYNTAX_ERR 異常。
- 嘗試從用戶提供的 URL 資源位置處獲取腳本資源。
- 對于 importScripts 方法的每一個參數(shù),按照用戶的提供順序,獲取腳本資源后繼續(xù)進行其它操作。
Worker線程聲明周期
worker線程間的數(shù)據(jù)傳遞必須依賴于瀏覽器的context環(huán)境,通過MessagePort進行傳遞數(shù)據(jù),所以每個worker線程的全局作用域都會有端口列表,并且會在WorkerGlobalScope中生成一個worker線程的線程列表,在初始化時為空。當worker線程創(chuàng)建時會被填充進去,當worker線程終止時會從這個列表刪除。
worker線程中可調(diào)用的對象
在worker線程中,可以獲得下列對象:
- navigator
- location
- XMLHttpRequest
- setTimeout/setInterval
- Application Cache
- fetch
- atob/btoa
等等。
實例
下面寫一個使用的小例子
html文件:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Document</title> </head> <body> <button id="btn">發(fā)送</button> <script>var worker = new Worker('./worker.js')btn.onclick = function(){worker.postMessage({a:1,b:2,c:3})}worker.onmessage = function(e){console.log('index-msg:', e)}worker.onerror = function(e) {console.log('index-err', e)e.preventDefault()} </script> </body> </html>worker.js文件
self.onmessage = function(e) {console.log('worker in:', e)self.postMessage('get postMessage!') }兼容性
還是有必要列一下Worker目前在瀏覽器上的兼容性:
可以看到支持的非常不錯。
總結
可以看到Web Worker的出現(xiàn)使得在 Web 進行多線程編程成為可能,對于高消耗、耗時長的操作可以放到woker里面去進行。
所以可以在以下應用場景使用:
- 使用專用線程進行數(shù)學運算
- 圖像處理
- 大量數(shù)據(jù)的檢索
- 背景數(shù)據(jù)分析
等。
博客名稱:王樂平博客
CSDN博客地址:http://blog.csdn.net/lecepin
本作品采用知識共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議進行許可。總結
以上是生活随笔為你收集整理的PWA(Progressive Web App)入门系列:(五)Web Worker的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java代码总结【1】_查询手机号码归属
- 下一篇: ORACLE SQL日期函数