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