到 HTML5 總是讓人津津樂道,太多的特性和有趣的 API 讓人耳目一新。
但是很多童鞋還停留在語義化的階段,忽視了 HTML5 的強勁之處。
一、明確 JavaScript 是單線程
JavaScript 語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。
聽起來有些匪夷所思,為什么不設計成多線程提高效率呢?我們可以假設一種場景:
假定 JavaScript 同時有兩個線程,一個線程在某個 DOM 節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為準?
這決定了它只能是單線程,否則會帶來很復雜的同步問題。
為了避免復雜性,從一誕生,JavaScript 就是單線程,這已經成了這門語言的核心特征,估計短期內很難改變。
二、新曙光:Web Worker
單線程始終是一個痛點,為了利用多核 CPU 的計算能力,HTML5 提出 Web Worker 標準,允許 JavaScript 腳本創建多個線程。
但是子線程完全受主線程控制,且不得操作 DOM。
Web Workers 是現代瀏覽器提供的一個 JavaScript 多線程解決方案,我們可以找到很多使用場景:
1.我們可以用 Web Worker 做一些大計算量的操作;
2.可以實現輪詢,改變某些狀態;
3.頁頭消息狀態更新,比如頁頭的消息個數通知;
4.高頻用戶交互,拼寫檢查,譬如:根據用戶的輸入習慣、歷史記錄以及緩存等信息來協助用戶完成輸入的糾錯、校正功能等
5.加密:加密有時候會非常地耗時,特別是如果當你需要經常加密很多數據的時候(比如,發往服務器前加密數據)。
6.預取數據:為了優化網站或者網絡應用及提升數據加載時間,你可以使用 Workers 來提前加載部分數據以備不時之需。
加密是一個使用 Web Worker 的絕佳場景,因為它并不需要訪問 DOM 或者利用其它魔法,它只是純粹使用算法進行計算而已。
隨著大眾對個人敏感數據的日益重視,信息安全和加密也成為重中之重。這可以從近期的 12306 用戶數據泄露事件中體現出來。
三、兼容性
四、基本概念
1.首先記得去判斷是否支持
2.創建一個新的 worker 很簡單
3.postMessage 用來發送消息,而 onmessage 用來監聽消息
在主線程中使用時,onmessage 和 postMessage() 必須掛在 worker 對象上,而在 worker中使用時不用這樣做。
原因是,在 worker 內部,worker 是有效的全局作用域。
4.異常處理:
5.終止 worker
worker 線程會被立即殺死,不會有任何機會讓它完成自己的操作或清理工作。
6.在 worker 線程中,workers 也可以調用自己的 close 方法進行關閉:
五、快速開始
為了快速掌握,我們來做一個小例子:項目結構如下
Html
main.js
Work.js
代碼很簡單,主線程發送:「寫的真好!」
web worker 收到消息,發現內容中含有「好」字,回傳給主線程:「謝謝支持」
六、局限性
1.在 worker 內,不能直接操作 DOM 節點,也不能使用 window 對象的默認方法和屬性。
然而我們可以使用大量 window 對象之下的東西,包括 WebSockets,IndexedDB 以及 FireFox OS 專用的 Data Store API 等數據存儲機制。
這里舉個例子,我們修改 main.js:
再來修改 work.js
這時候運行就會報出:
這是因為:worker.js 執行的上下文,與主頁面 HTML 執行時的上下文并不相同,最頂層的對象并不是 Window,woker.js 執行的全局上下文,而是 WorkerGlobalScope,下文我們具體說明。
2.workers 和主線程間的數據傳遞通過這樣的消息機制進行:
雙方都使用 postMessage() 方法發送各自的消息,使用 onmessage 事件處理函數來響應消息(消息被包含在 Message 事件的 data 屬性中)。
3.同源限制
分配給 Worker 線程運行的腳本文件,必須與主線程的腳本文件同源。
4.文件限制
Worker 線程無法讀取本地文件,即不能打開本機的文件系統(file://),它所加載的腳本,必須來自服務器。
5.不允許本地文件
Uncaught SecurityError: Failed to create a worker:
script at '(path)/worker.js'
cannot be accessed from origin 'null'.
那如何解決呢?我們可以啟動一個本地服務器,建議使用 http-server,簡單易用。
6.內容安全策略
因此普遍來說,worker 并不受限于創建它的document(或者父級 worker )的內容安全策略。
我們來舉個例子,假設一個 document 有如下頭部聲明:
Content-Security-Policy: script-src 'self'
這個聲明有一部分作用在于,禁止它內部包含的腳本代碼使用 eval() 方法。然而,如果腳本代碼創建了一個 worker,在 worker 上下文中執行的代碼卻是可以使用 eval() 的。
關于 CSP(Content security policy 內容安全策略),可以看我之前的這篇文章Web 安全之 CSP(Content Security Policy)。
有一個例外情況,即 worker 腳本的源如果是一個全局性的唯一的標識符(例如,它的 URL 指定了數據模式或者 blob),worker則會繼承創建它的 document 或者 worker 的 CSP。
七、擴展:WorkerGlobalScope
關于 ,我們可以在 MDN 上面找到文檔:
1.self:
我們可以使用 WorkerGlobalScope 的 self 屬性來獲取這個對象本身的引用。
2.location:
location 屬性返回當線程被創建出來的時候與之關聯的 WorkerLocation 對象,它表示用于初始化這個工作線程的腳步資源的絕對 URL,即使頁面被多次重定向后,這個 URL 資源位置也不會改變。
3.close:
關閉當前線程,與 terminate 作用類似。
4.caches:
當前上下文得 CacheStorage,確保離線可用,同時可以自定義請求的響應。
5.console:
支持 console 語法。
6.importScripts
我們可以通過 importScripts() 方法通過 url 在 worker 中加載庫函數。
7.XMLHttpRequest
有了它,才能發出 Ajax 請求。
8.可以使用:
還有很多 API 可以使用,這里就不一一舉例了。
八、異常處理
當 worker 出現運行中錯誤時,它的 onerror 事件處理函數會被調用。它會收到一個擴展了 ErrorEvent 接口的名為 error 的事件。
該事件不會冒泡并且可以被取消。
錯誤事件我們常用如下這三個關鍵信息:
九、參考文章
1.JavaScript 運行機制詳解:再談Event Loop
2.MDN Web worker
3.從webWorker到serviceWorker
4.淺談webWorker
十、熱門原創文章
1.HTML5 之地理定位(Geolocation)
2.Git 版本控制之 Git-Flow
3.你了解軟件測試嗎?
eb worker是一個在后臺運行的JavaScript程序,它不會影響當前頁面的用戶操作。 從事過Windows桌面編程的人,應該熟悉工作線程(Work Thread)的概念,該線程主要負責比較耗費時間的數據處理或通信方面的任務,當任務處理完成時,發送消息通知界面主線程(UI Thread)。 這樣的多線程機制可以大大提高應用程序處理數據的能力,我們在前言中說過HTML5的設計目的之一就是想盡可能結合傳統本地應用和網頁之間的優點,Web Worker就是這樣一個例子。文章最后給出了demo的訪問網址。
和C++(版本>=11)以及Go這些系統級語言內置支持多線程并行計算不同,網頁JavaScript腳本只支持單線程,如果在HTML頁面中執行腳本,那么該頁面將不能響應用戶交互,直到腳本執行完成。 那么如果不幸該腳本在等待某個網絡數據包或者正在執行一個大的循環處理,頁面就將處于僵死狀態。
Web Workers 是HTML5提供的一個JavaScript多線程解決方案。通過使用Web worker,執行任務交付給瀏覽器在后臺默默運行,而不會影響當前頁面的性能,用戶可以繼續任何其他操作,這將大大提升用戶體驗。
下面表格中的數字表示最早支持的版本號:
下面的例子演示了一個計數的網頁工作線程:
Step 1: 檢查瀏覽器支持
并非所有瀏覽器都支持該API,因此在創建Web worker之前,我們需要先檢測該特性是否被用戶瀏覽器所支持:
Step 2: 創建一個Web Worker文件
我們新建一個外部JavaScript文件,文件名為"demo_workers.js",代碼如下(簡單的計數):
上述代碼中的postMessage方法很重要,依賴于該方法,工作線程才能和HTML頁面(用戶界面)之間通信,該方法發送一個消息回給頁面。
Step 3:創建一個Web Worker對象
我們已經有了上述的web worker文件,現在我們需要從HTML頁面調用它。下面這行代碼檢查worker是否已存在,如果不存在,則創建一個新的Web Worker對象,并運行"demo_workers.js"文件中的代碼:
然后我們可以在Web worker中發送和接收消息。我們給該Web worker添加一個"onmessage"事件偵聽器。
當web worker發回一個消息時,頁面事件偵聽器中的代碼被執行。web worker發回的數據被保存在event.data中。
Step 4:終止Web Worker
當一個Web worker對象被創建后,它將持續偵聽消息(即使外部腳本已經執行完成),除非被終止。要終止一個web worker,釋放相關的瀏覽器/計算機資源,使用 terminate() 方法:
w.terminate();
Step 5:重用Web Worker
如果您在worker被終止后,將worker變量設置為未定義(undefined),就可以被重用:
w=undefined;
Web worker的代碼在外部.js文件中,HTML頁面的代碼如下:
Demo源碼可訪問:http://www.ikinsoft.com/3ddemo/webWork.html
下是針對高級前端工程師的HTML相關面試題:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。