JS多線程API?webworker應(yīng)用場景有哪些
以前我們總說,JS是單線程沒有多線程,當JS在頁面中運行長耗時同步任務(wù)的時候就會導(dǎo)致頁面假死影響用戶體驗,從而需要設(shè)置把任務(wù)放在任務(wù)隊列中;執(zhí)行任務(wù)隊列中的任務(wù)也并非多線程進行的,然而現(xiàn)在HTML5提供了我們前端開發(fā)這樣的能力 - Web Workers API,我們一起來看一看 Web Worker 是什么,怎么去使用它,在實際生產(chǎn)中如何去用它來進行產(chǎn)出。
1. 概述
Web Workers 使得一個Web應(yīng)用程序可以在與主執(zhí)行線程分離的后臺線程中運行一個腳本操作。這樣做的好處是可以在一個單獨的線程中執(zhí)行費時的處理任務(wù),從而允許主(通常是UI)線程運行而不被阻塞。
它的作用就是給JS創(chuàng)造多線程運行環(huán)境,允許主線程創(chuàng)建worker線程,分配任務(wù)給后者,主線程運行的同時worker線程也在運行,相互不干擾,在worker線程運行結(jié)束后把結(jié)果返回給主線程。這樣做的好處是主線程可以把計算密集型或高延遲的任務(wù)交給worker線程執(zhí)行,這樣主線程就會變得輕松,不會被阻塞或拖慢。這并不意味著JS語言本身支持了多線程能力,而是瀏覽器作為宿主環(huán)境提供了JS一個多線程運行的環(huán)境。
不過因為worker一旦新建,就會一直運行,不會被主線程的活動打斷,這樣有利于隨時響應(yīng)主線程的通性,但是也會造成資源的浪費,所以不應(yīng)過度使用,用完注意關(guān)閉?;蛘哒f:如果worker無實例引用,該worker空閑后立即會被關(guān)閉;如果worker實列引用不為0,該worker空閑也不會被關(guān)閉。
看一看它的兼容性
Browser | IE | Edge | FireFox | Chrome | Safari |
---|---|---|---|---|---|
version | 10+ | 12+ | 3.5+ | 4+ | 4+ |
2. 使用
2.1 限制
worker線程的使用有一些注意點
同源限制 worker線程執(zhí)行的腳本文件必須和主線程的腳本文件同源,這是當然的了,總不能允許worker線程到別人電腦上到處讀文件吧
文件限制 為了安全,worker線程無法讀取本地文件,它所加載的腳本必須來自網(wǎng)絡(luò),且需要與主線程的腳本同源
DOM操作限制 worker線程在與主線程的window不同的另一個全局上下文中運行,其中無法讀取主線程所在網(wǎng)頁的DOM對象,也不能獲取
document
、window
等對象,但是可以獲取navigator
、location(只讀)
、XMLHttpRequest
、setTimeout族
等瀏覽器API。通信限制 worker線程與主線程不在同一個上下文,不能直接通信,需要通過
postMessage
方法來通信。腳本限制 worker線程不能執(zhí)行
alert
、confirm
,但可以使用XMLHttpRequest
對象發(fā)出ajax請求。
2.2 web worker例子
在主線程中生成 Worker 線程很容易:
var myWorker = new Worker(jsUrl, options)
Worker()構(gòu)造函數(shù),第一個參數(shù)是腳本的網(wǎng)址(必須遵守同源政策),該參數(shù)是必需的,且只能加載 JS 腳本,否則報錯。第二個參數(shù)是配置對象,該對象可選。它的一個作用就是指定 Worker 的名稱,用來區(qū)分多個 Worker 線程。
// 主線程 var myWorker = new Worker('worker.js', {?name : 'myWorker' }); // Worker 線程 self.name // myWorker
關(guān)于api什么的,直接上例子大概就能明白了,首先是worker線程的js文件:
// workerThread1.js let i = 1 function simpleCount() { i++ self.postMessage(i) setTimeout(simpleCount, 1000) } simpleCount() self.onmessage = ev => { postMessage(ev.data + ' 呵呵~') }
在HTML文件中的body中:
Worker 輸出內(nèi)容: id='app'> type='text' title='' id='msg'> onclick='sendMessage()'>發(fā)送 onclick='stopWorker()'>stop!
可以自己運行一下看看效果,上面用到了一些常用的api
主線程中的api, worker
表示是 Worker 的實例:
worker.postMessage
: 主線程往worker線程發(fā)消息,消息可以是任意類型數(shù)據(jù),包括二進制數(shù)據(jù)worker.terminate
: 主線程關(guān)閉worker線程worker.onmessage
: 指定worker線程發(fā)消息時的回調(diào),也可以通過worker.addEventListener('message',cb)
的方式worker.onerror
: 指定worker線程發(fā)生錯誤時的回調(diào),也可以worker.addEventListener('error',cb)
Worker線程中全局對象為 self
,代表子線程自身,這時 this
指向 self
,其上有一些api:
self.postMessage
: worker線程往主線程發(fā)消息,消息可以是任意類型數(shù)據(jù),包括二進制數(shù)據(jù)self.close
: worker線程關(guān)閉自己self.onmessage
: 指定主線程發(fā)worker線程消息時的回調(diào),也可以self.addEventListener('message',cb)
self.onerror
: 指定worker線程發(fā)生錯誤時的回調(diào),也可以self.addEventListener('error',cb)
注意, w.postMessage(aMessage,transferList)
方法接受兩個參數(shù), aMessage
是可以傳遞任何類型數(shù)據(jù)的,包括對象,這種通信是拷貝關(guān)系,即是傳值而不是傳址,Worker 對通信內(nèi)容的修改,不會影響到主線程。事實上,瀏覽器內(nèi)部的運行機制是,先將通信內(nèi)容串行化,然后把串行化后的字符串發(fā)給 Worker,后者再將它還原。一個可選的 Transferable對象的數(shù)組,用于傳遞所有權(quán)。如果一個對象的所有權(quán)被轉(zhuǎn)移,在發(fā)送它的上下文中將變?yōu)椴豢捎?中止),并且只有在它被發(fā)送到的worker中可用??赊D(zhuǎn)移對象是如ArrayBuffer,MessagePort或ImageBitmap的實例對象, transferList
數(shù)組中不可傳入null。
更詳細的API參見 MDN - WorkerGlobalScope。
worker線程中加載腳本的api:
importScripts('script1.js') // 加載單個腳本 importScripts('script1.js', 'script2.js') // 加載多個腳本
3. 實戰(zhàn)場景
個人覺得,Web Worker我們可以當做計算器來用,需要用的時候掏出來摁一摁,不用的時候一定要收起來~
加密數(shù)據(jù)
有些加解密的算法比較復(fù)雜,或者在加解密很多數(shù)據(jù)的時候,這會非常耗費計算資源,導(dǎo)致UI線程無響應(yīng),因此這是使用Web Worker的好時機,使用Worker線程可以讓用戶更加無縫的操作UI。
預(yù)取數(shù)據(jù)
有時候為了提升數(shù)據(jù)加載速度,可以提前使用Worker線程獲取數(shù)據(jù),因為Worker線程是可以是用
XMLHttpRequest
的。預(yù)渲染
在某些渲染場景下,比如渲染復(fù)雜的canvas的時候需要計算的效果比如反射、折射、光影、材料等,這些計算的邏輯可以使用Worker線程來執(zhí)行,也可以使用多個Worker線程,這里有個射線追蹤的示例。
復(fù)雜數(shù)據(jù)處理場景
某些檢索、排序、過濾、分析會非常耗費時間,這時可以使用Web Worker來進行,不占用主線程。
預(yù)加載圖片
有時候一個頁面有很多圖片,或者有幾個很大的圖片的時候,如果業(yè)務(wù)限制不考慮懶加載,也可以使用Web Worker來加載圖片,可以參考一下這篇文章的探索,這里簡單提要一下。
// 主線程 let w = new Worker("js/workers.js"); w.onmessage = function (event) { var img = document.createElement("img"); img.src = window.URL.createObjectURL(event.data); document.querySelector('#result').appendChild(img) } // worker線程 let arr = [...好多圖片路徑]; for (let i = 0, len = arr.length; i < len; i++) { let req = new XMLHttpRequest(); req.open('GET', arr[i], true); req.responseType = "blob"; req.setRequestHeader("client_type", "DESKTOP_WEB"); req.onreadystatechange = () => { if (req.readyState == 4) { postMessage(req.response); } } req.send(null); }
在 Web Workers 實戰(zhàn)的時候注意:
雖然使用worker線程不會占用主線程,但是啟動worker會比較耗費資源
主線程中使用XMLHttpRequest在請求過程中瀏覽器另開了一個異步http請求線程,但是交互過程中還是要消耗主線程資源
以上就是JS多線程API webworker應(yīng)用場景有哪些的詳細內(nèi)容,更多關(guān)于JS多線程webworker應(yīng)用場景的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript中的Math.atan2()方法使用詳解
這篇文章主要介紹了JavaScript中的Math.atan2()方法使用詳解,是JS入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-06-06JavaScript高級程序設(shè)計(第3版)學(xué)習(xí)筆記3 js簡單數(shù)據(jù)類型
數(shù)據(jù)類型是編程語言的磚瓦,是所有你能想象到的復(fù)雜抽象的基礎(chǔ),在現(xiàn)代編程語言中,除了語言本身內(nèi)置的一些簡單數(shù)據(jù)類型外,基本上都提供了用于自定義數(shù)據(jù)類型的語言機制(在C中也可以利用結(jié)構(gòu)體來實現(xiàn)),這些機制在一定程度上也決定了該語言的流行度和生命力2012-10-10