您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關HTML5中怎么實現多線程,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
一、明確 JavaScript 是單線程
JavaScript 語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。
聽起來有些匪夷所思,為什么不設計成多線程提高效率呢?我們可以假設一種場景:
假定 JavaScript
同時有兩個線程,一個線程在某個 DOM
節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為準?
作為瀏覽器腳本語言, JavaScript
的主要用途是與用戶互動,以及操作 DOM
。
這決定了它只能是單線程,否則會帶來很復雜的同步問題。為了避免復雜性,從一誕生, JavaScript
就是單線程,這已經成了這門語言的核心特征,估計短期內很難改變。
二、新曙光:Web Worker
單線程始終是一個痛點,為了利用多核 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
。
七、擴展: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.可以使用:
setTimeout/setInterval
addEventListener/postMessage
還有很多 API
可以使用,這里就不一一舉例了。
八、異常處理
當 worker
出現運行中錯誤時,它的 onerror
事件處理函數會被調用。它會收到一個擴展了 ErrorEvent
接口的名為 error
的事件。該事件不會冒泡并且可以被取消。
為了防止觸發默認動作,worker 可以調用錯誤事件的 preventDefault() 方法。
錯誤事件我們常用如下這三個關鍵信息:
Message:可讀性良好的錯誤消息;
Filename:發生錯誤的腳本文件名;
Lineno:發生錯誤時所在腳本文件的行號;
worker.onerror = function(error) { console.log(error.message); throw error; };
關于HTML5中怎么實現多線程就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。