如何使用 Web Worker 為 JS 創(chuàng)造多線程環(huán)境屯掖?

Web Worker是什么

我們都知道JS是單線程的,所有任務(wù)在一個線程上襟衰,一次只能做一件事贴铜。雖然可以通過AJAX、定時器等可以實(shí)現(xiàn)"并行"瀑晒,但還是沒有改變JS單線程的本質(zhì)绍坝,把一些復(fù)雜的運(yùn)算放在頁面上執(zhí)行,還是會導(dǎo)致很卡苔悦,甚至卡死

而HTML5標(biāo)準(zhǔn)中的Web Worker為JS創(chuàng)造多線程環(huán)境轩褐,允許主線程創(chuàng)建Worker線程并給它分配任務(wù),而且在主線程執(zhí)行任務(wù)的時候玖详,worker線程可以同時在后臺執(zhí)行它的任務(wù)灾挨,互不干擾

這讓我們可以將一些復(fù)雜運(yùn)算、高頻輸入的響應(yīng)處理竹宋、大文件分片上傳等放在worker線程處理,最后再返回給主線程地技。很大程度上緩解了主線程UI渲染阻塞的問題蜈七,頁面就會很流暢

使用 Web Worker 有幾個注意點(diǎn):

  • 同源限制:worker線程運(yùn)行的文件必須和主線程的腳本是同源
  • 文件限制:worker線程不能打開本機(jī)的文件系統(tǒng)(file://),只能加載網(wǎng)絡(luò)文件
  • 其他限制:worker線程不能操作DOM莫矗、不能使用document飒硅、window、parent對象和alert作谚、confirm方法

但可以使用location三娩、navigator,不過只能只讀不能改寫妹懒;還可以使用XMLHttpRequest發(fā)送AJAX請求雀监;還可以使用緩存

怎么使用 Web Worker呢?

分別看看在主線程的頁面和 worker 線程中的方法和用法

主線程中的方法和使用

創(chuàng)建 worker 進(jìn)程眨唬,直接 new 就完事兒了会前,而且主線程內(nèi)和 worker 線程內(nèi)都可以創(chuàng)建多個 worker 線程

可以傳兩個參數(shù),第一個是網(wǎng)絡(luò)腳本文件的鏈接匾竿,不能是本地路徑瓦宜,第二個是配置對象,不是必填

假設(shè)引入了一個網(wǎng)絡(luò)文件 worker1.js

// 主線程
const worker = new Worker('http://worker1.js')

然后使用 worker.postMessage() 方法向 worker 線程發(fā)送數(shù)據(jù)岭妖,參數(shù)可以是各種數(shù)據(jù)類型临庇,包括二進(jìn)制反璃。注意!發(fā)送過去的數(shù)據(jù)是傳值假夺,也就是拷貝關(guān)系而不是傳址淮蜈,所以 worker 線程內(nèi)部無論怎么修改,都不會影響到主線程的數(shù)據(jù)

worker.postMessage('這是發(fā)給worker線程的消息')

然后再通過 worker.onmessage 監(jiān)聽并接收 worker 線程處理完返回的數(shù)據(jù)

worker.onmessage = function(event){
    const data = event.data // 這是返回的數(shù)據(jù)
}

如果 worker 線程中內(nèi)部發(fā)生錯誤侄泽,會觸發(fā)主線程的 onerror 事件

worker.onerror( event => {
    console.log( 'worker 線程內(nèi)部執(zhí)行報錯了', event )
})
// 或者
worker.onerrer = function( event ){
    console.log( 'worker 線程內(nèi)部執(zhí)行報錯了', event )
}
// 或者
worker.addEventListener('error', event => {
    console.log( 'worker 線程內(nèi)部執(zhí)行報錯了', event )
})

最后是關(guān)掉 worker 線程礁芦,因?yàn)?worker 線程創(chuàng)建成功就會始終運(yùn)行,不會主動關(guān)閉悼尾,所以我們不用的時候要主動關(guān)掉柿扣,避免浪費(fèi)資源

worker.terminate() // 這樣 worker 線程就關(guān)閉了

worker 線程中的方法和使用

worker 線程內(nèi)部執(zhí)行上下文跟主線程的執(zhí)行上下文 window 不一樣,是一個叫 WorkerGlobalScope 的東西闺魏,我們可以用它或者 self 來訪問全局對象

如果主線程創(chuàng)建 worker 線程的時候有傳第二個參數(shù)未状,在 worker1.js 里面,也就是在 worker 線程里可以直接這樣獲取析桥,比如區(qū)分多個 worker 線程的時候

// 主線程
const worker = new Worker('http://worker1.js', { name: 'worker1' })

// worker1.js
self.name // worker1  

worker 線程里面司草,需要監(jiān)聽并接收主線程發(fā)送過來的數(shù)據(jù)。

self.addEventListener('message', event => {
    const data = event.data // 接收到的數(shù)據(jù) 
    ...
    self.postMessage( '這是處理好的數(shù)據(jù)' ) // 將處理好的數(shù)據(jù)發(fā)送給主線程
}, false)
// 或者 不要 self 也可以
addEventListener('message', event => {
    const data = event.data // 接收到的數(shù)據(jù) 
    ...
    self.postMessage( '這是處理好的數(shù)據(jù)' ) // 將處理好的數(shù)據(jù)發(fā)送給主線程
}, false)

如果 worker 線程內(nèi)部需要加載其他腳本泡仗,只能用importScripts()方法埋虹,路徑還是只能網(wǎng)絡(luò)文件,不能使用本地的

importScripts('http://worker2.js', 'http://worker3.js') // 可以引入多個

worker 線程內(nèi)部也可以使用 onerror 監(jiān)聽錯誤娩怎,和主線程使用方式一樣搔课。

worker 線程內(nèi)部也可以關(guān)閉 worker 線程,方法和主線程不一樣

self.close() // 這樣就從內(nèi)部關(guān)閉了

worker 線程能不能直接寫在主線程的頁面里

能截亦!

既然它要接收一個網(wǎng)絡(luò)地址URL爬泥,就給它創(chuàng)建一個URL

首先給 script 標(biāo)簽起一個瀏覽器不認(rèn)識的type,然后將這個標(biāo)簽里的內(nèi)容轉(zhuǎn)成二進(jìn)制對象崩瓤,然后為這個對象生成URL袍啡,再用這個URL創(chuàng)建 worker 線程,如下

<!DOCTYPE html>
    <body>
        <div>這是頁面</div>
        <script id="worker" type="xxxxx">
            // worker 線程
            addEventListener('message', event => {
                console.log(event.data) // 收到了嗎
                postMessage('收到了') // 發(fā)送給主線程
            })
        </script>
        <script type="text/javascript">
            // 主線程
            const blob = new Blob([ document.querySelector('#worker').textContent ])
            const url = window.URL.createObjectURL(blob)
            const worker = new Worker(url)
            worker.postMessage('收到了嗎')
            worker.onmessage = event => {
                console.log(event.data) // 收到了
            }
        </script>
    </body>
</html>

worker 線程輪詢

function createWorker(fn){
    const blob = new Blob([fn.toString()])
    const url = window.URL.createObjectURL(blob)
    const worker = new Worker(url)
    return worker
}

const webWorker = createWorker(function(){
    let cache;
    
    function compare(new, old){ ... }
    
    setInterval(()=>{
        fetch('/api/xxx').then(res=>{
            let data = res.data
            if(!compare(data, cache)){
                cache = data
                self.postMessage(data)
            }
        })
    },1000)
})

結(jié)語

點(diǎn)贊支持却桶、手留余香境输、與有榮焉

參考

# Web Worker 使用教程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娩贷,一起剝皮案震驚了整個濱河市奢驯,隨后出現(xiàn)的幾起案子酣藻,更是在濱河造成了極大的恐慌打却,老刑警劉巖赃蛛,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晌砾,死亡現(xiàn)場離奇詭異瓶逃,居然都是意外死亡梦皮,警方通過查閱死者的電腦和手機(jī)偷拔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門蒋院,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亏钩,“玉大人,你說我怎么就攤上這事欺旧」贸螅” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵辞友,是天一觀的道長栅哀。 經(jīng)常有香客問我,道長称龙,這世上最難降的妖魔是什么留拾? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮鲫尊,結(jié)果婚禮上痴柔,老公的妹妹穿的比我還像新娘。我一直安慰自己疫向,他們只是感情好咳蔚,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搔驼,像睡著了一般谈火。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舌涨,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天堆巧,我揣著相機(jī)與錄音,去河邊找鬼泼菌。 笑死,一個胖子當(dāng)著我的面吹牛啦租,可吹牛的內(nèi)容都是我干的哗伯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼篷角,長吁一口氣:“原來是場噩夢啊……” “哼焊刹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恳蹲,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤虐块,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嘉蕾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贺奠,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年错忱,在試婚紗的時候發(fā)現(xiàn)自己被綠了儡率。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挂据。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖儿普,靈堂內(nèi)的尸體忽然破棺而出崎逃,到底是詐尸還是另有隱情,我是刑警寧澤眉孩,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布个绍,位于F島的核電站,受9級特大地震影響浪汪,放射性物質(zhì)發(fā)生泄漏巴柿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一吟宦、第九天 我趴在偏房一處隱蔽的房頂上張望篮洁。 院中可真熱鬧,春花似錦殃姓、人聲如沸袁波。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篷牌。三九已至,卻和暖如春踏幻,著一層夾襖步出監(jiān)牢的瞬間枷颊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工该面, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留夭苗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓隔缀,卻偏偏與公主長得像题造,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子猾瘸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容