提到HTML5 總是讓人津津樂道,太多的特性和有趣的API 讓人耳目一新。但很多童鞋還停留在語意化的階段,忽略了HTML5 的強烈之處。
這節我們來探討一下多執行緒Web-Worker。
一、明確JavaScript 是單線程JavaScript 語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。
聽起來有些匪夷所思,為什麼不設計成多執行緒提高效率呢?我們可以假設一種場景:
假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為準?
作為瀏覽器腳本語言, JavaScript的主要用途是與使用者互動,以及操作DOM 。
這決定了它只能是單線程,否則會帶來複雜的同步問題。為了避免複雜性,從一誕生, JavaScript就是單線程,這已經成了這門語言的核心特徵,估計短期內很難改變。
單執行緒總是一個痛點,為了利用多核心CPU的運算能力, HTML5提出Web Worker標準,讓JavaScript腳本建立多個執行緒。但是子執行緒完全受主執行緒控制,且不得操作DOM 。
所以,這個新標準並沒有改變JavaScript單執行緒的本質。
Web Workers是現代瀏覽器提供的JavaScript多執行緒解決方案,我們可以找到很多使用場景:
1.我們可以用Web Worker做一些大計算量的操作;
2.可以實現輪詢,改變某些狀態;
3.頁頭訊息狀態更新,例如頁頭的訊息個數通知;
4.高頻使用者交互,拼字檢查,譬如:根據使用者的輸入習慣、歷史記錄以及快取等資訊來協助使用者完成輸入的糾錯、校正功能等
5.加密:加密有時會非常耗時,特別是如果當你需要經常加密很多資料的時候(例如,發送到伺服器前加密資料)。
6.預取資料:為了優化網站或網路應用程式及提升資料載入時間,你可以使用Workers
來提前載入部分資料以備不時之需。
加密是使用Web Worker的絕佳場景,因為它並不需要存取DOM或利用其它魔法,它只是純粹使用演算法進行計算而已。隨著大眾對個人敏感資料的日益重視,資訊安全和加密也成為重中之重。這可以從近期的12306 用戶資料外洩事件中體現出來。
一旦在Worker 進行計算,它對於用戶來說是無縫且不會影響用戶體驗。
三、相容性四、基本概念1.首先記得判斷是否支持
if (window.Worker) { ...} 2.創建一個新的worker很簡單
const myWorker = new Worker('worker.js');postMessage() 方法和onmessage 事件處理函數是Workers 的黑魔法。
3. postMessage用來傳送訊息,而onmessage用來監聽訊息
const worker = new Worker('src/worker.js');worker.onmessage = e => { console.log(e.data);};worker.postMessage('你好嗎!');在主執行緒中使用時, onmessage和postMessage()必須掛在worker物件上,而在worker中使用時不用這樣做。原因是,在worker內部, worker是有效的全域作用域。
4.異常處理:
worker.onerror = function(error) { console.log(error.message); throw error;}; 5.終止worker
worker.terminate();
worker執行緒會立即被殺死,不會有任何機會讓它完成自己的操作或清理工作。
6.在worker執行緒中, workers也可以呼叫自己的close方法來關閉:
close();五、快速開始
為了快速掌握,我們來做一個小例子:專案架構如下
├── index.html└── src ├── main.js └── worker.js
Html
<html><head> <title>Web Work Demo</title> <meta charset=UTF-8 /></head><body> <div id=app> Hello Jartto! </div> <script src=src /main.js></script></body></html>
main.js
const worker = new Worker('src/worker.js');worker.onmessage = e => { const message = e.data; console.log(`[From Worker]: ${message}`); document.getElementById ('app').innerHTML = message;};worker.postMessage('寫的真好!');Work.js
onmessage = e => { const message = e.data; console.log(`[From Main]: ${message}`); if(message.indexOf('好') > -1) { postMessage('謝謝支持'); }};程式碼很簡單,主線程發送:「寫的真好!」
web worker 收到訊息,發現內容中含有「好」字,回傳給主線程:「謝謝支援」
六、局限性1.在worker內,不能直接操作DOM節點,也不能使用window物件的預設方法和屬性。然而我們可以使用大量window物件之下的東西,包括WebSockets , IndexedDB以及FireFox OS專用的Data Store API等資料儲存機制。
這裡舉個例子,我們修改main.js :
const worker = new Worker('src/worker.js');worker.onmessage = e => { const message = e.data; console.log(`[From Worker]: ${message}`); document.getElementById ('app').innerHTML = message;};+ worker.onerror = function(error) {+ console.log(error);+ worker.terminate();+ };worker.postMessage('寫的真好!');再來修改work.js
+ alert('jartto');onmessage = e => { const message = e.data; console.log(`[From Main]: ${message}`); if(message.indexOf('好') > - 1) { postMessage('謝謝支持'); }};這時候運行就會報出:
這是因為: 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'.
Chrome doesn 't let you load web workers when running scripts from a local file.
那如何解決呢?我們可以啟動一個本機伺服器,建議使用http-server ,簡單易用。
6.內容安全策略
有別於創建它的document對象, worker有它自己的執行上下文。因此普遍來說, worker並不受限於創建它的document (或父級worker )的內容安全策略。
我們來舉個例子,假設一個document有以下頭部聲明:
Content-Security-Policy: script-src 'self'
這個聲明有一部分作用在於,禁止它內部所包含的腳本程式碼使用eval()方法。然而,如果腳本程式碼建立了一個worker ,在worker上下文中執行的程式碼卻是可以使用eval()的。
為了給worker 指定CSP,必須為傳送worker 程式碼的請求本身加上一個CSP。
有一個例外情況,即worker腳本的來源如果是一個全域性的唯一的識別碼(例如,它的URL指定了資料模式或blob ), worker則會繼承創建它的document或者worker的CSP 。
關於,我們可以在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的事件。該事件不會冒泡並且可以被取消。
為了防止觸發預設動作,worker 可以呼叫錯誤事件的preventDefault() 方法。
錯誤事件我們常用以下這三個關鍵訊息:
worker.onerror = function(error) { console.log(error.message); throw error;};以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。