摘要:監(jiān)聽線程返回的信息父進(jìn)程接收的數(shù)據(jù)主線程關(guān)閉線程線程一旦新建成功,就會(huì)始終運(yùn)行,這樣有利于隨時(shí)響應(yīng)主線程的通信。幸運(yùn)的是,提供了一中轉(zhuǎn)移數(shù)據(jù)的方式,允許主線程把二進(jìn)制數(shù)據(jù)直接轉(zhuǎn)移給子線程。
我們都知道,JavaScript 是單線程的,在同一時(shí)刻只能處理一個(gè)任務(wù),我們會(huì)通過 setTimeout()、setInterval()、ajax 和事件處理程序等技術(shù)模擬“并行”。但都不是真正意義上的并行:
Web Worker 是 HTML5 標(biāo)準(zhǔn)的一部分,這一規(guī)范定義了一套 API,它允許一段 JavaScript 程序運(yùn)行在主線程之外的另外一個(gè)線程中。
這在很大程度上利用了現(xiàn)在不斷升級(jí)的電腦計(jì)算能力:能夠在同一時(shí)間平行處理兩個(gè)任務(wù)。
游泳、健身了解一下:博客、前端積累文檔、公眾號(hào)、GitHub場(chǎng)景
當(dāng)我們有些任務(wù)需要花費(fèi)大量的時(shí)間,進(jìn)行復(fù)雜的運(yùn)算,就會(huì)導(dǎo)致頁面卡死:用戶點(diǎn)擊頁面需要很長的時(shí)間才能響應(yīng),因?yàn)榍懊娴娜蝿?wù)還未完成,后面的任務(wù)只能排隊(duì)等待。對(duì)用戶來說,這樣的體驗(yàn)無疑是糟糕的,web worker 就是為了解決這種花費(fèi)大量時(shí)間的復(fù)雜運(yùn)算而誕生的!
WebWorker 的作用:創(chuàng)建 worker 線程WebWorker 允許在主線程之外再創(chuàng)建一個(gè) worker 線程,在主線程執(zhí)行任務(wù)的同時(shí),worker 線程也可以在后臺(tái)執(zhí)行它自己的任務(wù),互不干擾。
這樣就讓 JS 變成多線程的環(huán)境了,我們可以把高延遲、花費(fèi)大量時(shí)間的運(yùn)算,分給 worker 線程,最后再把結(jié)果返回給主線程就可以了,因?yàn)闀r(shí)間花費(fèi)多的任務(wù)被 web worker 承擔(dān)了,主線程就會(huì)很流暢了!
主線程 我們先來看一下栗子:codepen,這里我寫了一個(gè) class,里面有詳細(xì)注釋,可以參考一下。
創(chuàng)建 worker 對(duì)象:主線程調(diào)用new Worker()構(gòu)造函數(shù),新建一個(gè) worker 線程,構(gòu)造函數(shù)的參數(shù)是一個(gè) url,生成這個(gè) url 的方法有兩種:
腳本文件:
const worker = new Worker("https://~.js");
因?yàn)?worker 的兩個(gè)限制:
分配給 Worker 線程運(yùn)行的腳本文件,必須與主線程的腳本文件同源。
worker 不能讀取本地的文件(不能打開本機(jī)的文件系統(tǒng)file://),它所加載的腳本必須來自網(wǎng)絡(luò)。
可以看到限制還是比較多的,如果要使用這種形式的話,在項(xiàng)目中推薦把文件放在靜態(tài)文件夾中,打包的時(shí)候直接拷貝進(jìn)去,這樣我們就可以拿到固定的鏈接了,
字符串形式:
const data = ` // worker線程 do something `; // 轉(zhuǎn)成二進(jìn)制對(duì)象 const blob = new Blob([data]); // 生成url const url = window.URL.createObjectURL(blob); // 加載url const worker = new Worker(url);
栗子中就是使用這種形式的,方便我們演示。
在項(xiàng)目中:我們可以把worker線程的邏輯寫在js文件里面,然后字符串化,然后再export、import,配合webpack進(jìn)行模塊化管理,這樣就很容易使用了。
主線程的其他 API: 1. 主線程與 worker 線程通信:worker.postMessage({ hello: ["hello", "world"] });
它們相互之間的通信可以傳遞對(duì)象和數(shù)組,這樣我們就可以根據(jù)相互之間傳遞的信息來進(jìn)行一些操作,比如可以設(shè)置一個(gè)type屬性,當(dāng)值為hello時(shí)執(zhí)行什么函數(shù),當(dāng)值為world的時(shí)候執(zhí)行什么函數(shù)。
值得注意的是:它們之間通信是通過拷貝的形式來傳遞數(shù)據(jù)的,進(jìn)行傳遞的對(duì)象需要經(jīng)過序列化,接下來在另一端還需要反序列化。這就意味著:
我們不能傳遞不能被序列化的數(shù)據(jù),比如函數(shù),會(huì)拋出錯(cuò)誤的。
在一端改變數(shù)據(jù),另外一端不會(huì)受影響,因?yàn)閿?shù)據(jù)不存在引用,是拷貝過來的。
2. 監(jiān)聽 worker 線程返回的信息worker.onmessage = function (e) { console.log("父進(jìn)程接收的數(shù)據(jù):", e.data); // doSomething(); }3. 主線程關(guān)閉 worker 線程
Worker 線程一旦新建成功,就會(huì)始終運(yùn)行,這樣有利于隨時(shí)響應(yīng)主線程的通信。
這也是 Worker 比較耗費(fèi)計(jì)算機(jī)的計(jì)算資源(CPU)的原因,一旦使用完畢,就應(yīng)該關(guān)閉 worker 線程。
worker.terminate(); // 主線程關(guān)閉worker線程4. 監(jiān)聽錯(cuò)誤
// worker線程報(bào)錯(cuò) worker.onerror = e => { // e.filename - 發(fā)生錯(cuò)誤的腳本文件名;e.lineno - 出現(xiàn)錯(cuò)誤的行號(hào);以及 e.message - 可讀性良好的錯(cuò)誤消息 console.log("onerror", e); };
也可以像我給出的栗子一樣,把兩個(gè)報(bào)錯(cuò)放在一起寫,有報(bào)錯(cuò)把信息傳出來就好了。
Worker 線程 self 代表 worker 進(jìn)程自身worker 線程的執(zhí)行上下文是一個(gè)叫做WorkerGlobalScope的東西跟主線程的上下文(window)不一樣。
我們可以使用self/WorkerGlobalScope來訪問全局對(duì)象。
監(jiān)聽主線程傳過來的信息:self.onmessage = e => { console.log("主線程傳來的信息:", e.data); // do something };發(fā)送信息給主線程
self.postMessage({ hello: [ "這條信息", "來自worker線程" ] });worker 線程關(guān)閉自身
self.close()worker 線程加載腳本:
Worker 線程能夠訪問一個(gè)全局函數(shù) imprtScripts()來引入腳本,該函數(shù)接受 0 個(gè)或者多個(gè) URI 作為參數(shù)。
importScripts("http~.js","http~2.js");
腳本中的全局變量都能被 worker 線程使用。
腳本的下載順序是不固定的,但執(zhí)行時(shí)會(huì)按照傳入 importScripts() 中的文件名順序進(jìn)行,這個(gè)過程是同步的。
Worker 線程限制因?yàn)?worker 創(chuàng)造了另外一個(gè)線程,不在主線程上,相應(yīng)的會(huì)有一些限制,我們無法使用下列對(duì)象:
window 對(duì)象
document 對(duì)象
DOM 對(duì)象
parent 對(duì)象
我們可以使用下列對(duì)象/功能:
瀏覽器:navigator 對(duì)象
URL:location 對(duì)象,只讀
發(fā)送請(qǐng)求:XMLHttpRequest 對(duì)象
定時(shí)器:setTimeout/setInterval,在 worker 線程輪詢也是很棒!
應(yīng)用緩存:Application Cache
多個(gè) worker 線程在主線程內(nèi)可以創(chuàng)建多個(gè) worker 線程
栗子最下方有。
worker 線程內(nèi)還可以新建 worker 線程,使用同源的腳本文件創(chuàng)建。
在 worker 線程內(nèi)再新建 worker 線程就不能使用window.URL.createObjectURL(blob),需要使用同源的腳本文件來創(chuàng)建新的 worker 線程,因?yàn)槲覀儫o法訪問到window對(duì)象。
這里不方便演示,跟在主線程創(chuàng)建 worker 線程是一個(gè)套路,只是改成了腳本文件形式創(chuàng)建 worker 線程。
線程間轉(zhuǎn)移二進(jìn)制數(shù)據(jù)因?yàn)橹骶€程與 worker 線程之間的通信是拷貝關(guān)系,當(dāng)我們要傳遞一個(gè)巨大的二進(jìn)制文件給 worker 線程處理時(shí)(worker 線程就是用來干這個(gè)的),這時(shí)候使用拷貝的方式來傳遞數(shù)據(jù),無疑會(huì)造成性能問題。
幸運(yùn)的是,Web Worker 提供了一中轉(zhuǎn)移數(shù)據(jù)的方式,允許主線程把二進(jìn)制數(shù)據(jù)直接轉(zhuǎn)移給子線程。這種方式比原先拷貝的方式,有巨大的性能提升。
一旦數(shù)據(jù)轉(zhuǎn)移到其他線程,原先線程就無法再使用這些二進(jìn)制數(shù)據(jù)了,這是為了防止出現(xiàn)多個(gè)線程同時(shí)修改數(shù)據(jù)的麻煩局面
下方栗子出自淺談 HTML5 Web Worker
// 創(chuàng)建二進(jìn)制數(shù)據(jù) var uInt8Array = new Uint8Array(1024*1024*32); // 32MB for (var i = 0; i < uInt8Array .length; ++i) { uInt8Array[i] = i; } console.log(uInt8Array.length); // 傳遞前長度:33554432 // 字符串形式創(chuàng)建worker線程 var myTask = ` onmessage = function (e) { var data = e.data; console.log("worker:", data); }; `; var blob = new Blob([myTask]); var myWorker = new Worker(window.URL.createObjectURL(blob)); // 使用這個(gè)格式(a,[a]) 來轉(zhuǎn)移二進(jìn)制數(shù)據(jù) myWorker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]); // 發(fā)送數(shù)據(jù)、轉(zhuǎn)移數(shù)據(jù) console.log(uInt8Array.length); // 傳遞后長度:0,原先線程內(nèi)沒有這個(gè)數(shù)據(jù)了
二進(jìn)制數(shù)據(jù)有:File、Blob、ArrayBuffer 等類型,也允許在 worker 線程之間發(fā)送,這對(duì)于影像處理、聲音處理、3D 運(yùn)算等就非常方便了,不會(huì)產(chǎn)生性能負(fù)擔(dān)應(yīng)用場(chǎng)景:
數(shù)學(xué)運(yùn)算
圖像、影音等文件處理
大量數(shù)據(jù)檢索
比如用戶輸入時(shí),我們?cè)诤笈_(tái)檢索答案,或者幫助用戶聯(lián)想,糾錯(cuò)等操作.
耗時(shí)任務(wù)都丟到 webworker 解放我們的主線程。
兼容:沒有找到具體的制定日期,有篇博客是在 10 年的 7 月份寫的,也就是說 web worker 至少出現(xiàn)了八年了,以下兼容摘自MDN:
Chrome:4, Firefox:3.5, IE:10.0, Opera:10.6, Safari:4
現(xiàn)在兼容還是做的比較好的,如果實(shí)在不放心的話:
if (window.Worker) { ... }else{ ... }結(jié)語:
Web Worker的出現(xiàn),給瀏覽器帶來了后臺(tái)計(jì)算的能力,把耗時(shí)的任務(wù)分配給worker線程來做,在很大程度上緩解了主線程UI渲染阻塞的問題,提升頁面性能。
使用起來也不復(fù)雜,以后有復(fù)雜的問題,記得要丟給我們?yōu)g覽器的后臺(tái)(web worker)來處理
看完之后,一定要研究一下文中的栗子,自己鼓搗鼓搗,實(shí)踐出真知!
PS: 推薦一下我上個(gè)月寫的手摸手教你使用WebSocket,感興趣的可以看一下。
希望看完的朋友可以點(diǎn)個(gè)喜歡/關(guān)注,您的支持是對(duì)我最大的鼓勵(lì)。博客、前端積累文檔、公眾號(hào)、GitHub
以上2018.11.25
參考資料:
MDN
Web Worker 使用教程
淺談HTML5 Web Worker
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/99434.html
摘要:半路出家的前端程序員應(yīng)該不在少數(shù),我也是其中之一。年,馮馮同事兼師兄看我寫太費(fèi)勁,跟我說對(duì)面樓在找,問我要不要學(xué),說出來可能有點(diǎn)丟人,但是在那之前,我真得不知道什么是,什么是。 半路出家的前端程序員應(yīng)該不在少數(shù),我也是其中之一。 為何會(huì)走向前端 非計(jì)算機(jī)專業(yè)的我,畢業(yè)之后,就職于一家電力行業(yè)公司,做過設(shè)備調(diào)試、部門助理、測(cè)試,也寫過一段時(shí)間的QT,那三年的時(shí)間,最難過的不是工作忙不忙,...
摘要:淺談前言都知道是單線程語言,最讓人頭疼的莫過于在網(wǎng)絡(luò)正常的情況下經(jīng)常出現(xiàn)頁面的假死,以及在進(jìn)行大量的循環(huán)計(jì)算時(shí)會(huì)導(dǎo)致線程阻塞由于要進(jìn)行大量的計(jì)算后面的運(yùn)行會(huì)被阻隔在此處,使得性能較差,代碼維護(hù)性差等一系列的問題發(fā)生。 WebWork淺談 前言: 都知道JS是單線程語言,最讓人頭疼的莫過于在網(wǎng)絡(luò)正常的情況下經(jīng)常出現(xiàn)頁面的假死, 以及在進(jìn)行大量的for循環(huán)計(jì)算時(shí)會(huì)導(dǎo)致線程阻塞,由于要進(jìn)行...
摘要:淺談前言都知道是單線程語言,最讓人頭疼的莫過于在網(wǎng)絡(luò)正常的情況下經(jīng)常出現(xiàn)頁面的假死,以及在進(jìn)行大量的循環(huán)計(jì)算時(shí)會(huì)導(dǎo)致線程阻塞由于要進(jìn)行大量的計(jì)算后面的運(yùn)行會(huì)被阻隔在此處,使得性能較差,代碼維護(hù)性差等一系列的問題發(fā)生。 WebWork淺談 前言: 都知道JS是單線程語言,最讓人頭疼的莫過于在網(wǎng)絡(luò)正常的情況下經(jīng)常出現(xiàn)頁面的假死, 以及在進(jìn)行大量的for循環(huán)計(jì)算時(shí)會(huì)導(dǎo)致線程阻塞,由于要進(jìn)行...
摘要:淺談前言都知道是單線程語言,最讓人頭疼的莫過于在網(wǎng)絡(luò)正常的情況下經(jīng)常出現(xiàn)頁面的假死,以及在進(jìn)行大量的循環(huán)計(jì)算時(shí)會(huì)導(dǎo)致線程阻塞由于要進(jìn)行大量的計(jì)算后面的運(yùn)行會(huì)被阻隔在此處,使得性能較差,代碼維護(hù)性差等一系列的問題發(fā)生。 WebWork淺談 前言: 都知道JS是單線程語言,最讓人頭疼的莫過于在網(wǎng)絡(luò)正常的情況下經(jīng)常出現(xiàn)頁面的假死, 以及在進(jìn)行大量的for循環(huán)計(jì)算時(shí)會(huì)導(dǎo)致線程阻塞,由于要進(jìn)行...
閱讀 2077·2021-11-16 11:45
閱讀 578·2021-11-04 16:12
閱讀 1379·2021-10-08 10:22
閱讀 858·2021-09-23 11:52
閱讀 4142·2021-09-22 15:47
閱讀 3521·2021-09-22 15:07
閱讀 492·2021-09-03 10:28
閱讀 1737·2021-09-02 15:21