HTML5之多線程(Web Worker)

這篇文章主要介紹了HTML5之多線程(Web Worker)的相關資料,寫的十分的全面細緻,具有一定的參考價值,對此有需要的朋友可以參考學習下。如有不足之處,歡迎批評指正。

提到 HTML5 總是讓人津津樂道,太多的特性和有趣的 API 讓人耳目一新。但是很多童鞋還停留在語義化的階段,忽視了 HTML5 的強勁之處。
這節我們來探討一下多線程 Web-Worker。

一、明確 JavaScript 是單線程

JavaScript 語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。
聽起來有些匪夷所思,爲什麼不設計成多線程提高效率呢?我們可以假設一種場景:
假定 JavaScript 同時有兩個線程,一個線程在某個 DOM 節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程爲準?
作爲瀏覽器腳本語言, JavaScript 的主要用途是與用戶互動,以及操作 DOM 。
這決定了它只能是單線程,否則會帶來很複雜的同步問題。爲了避免複雜性,從一誕生, JavaScript 就是單線程,這已經成了這門語言的核心特徵,估計短期內很難改變。

二、新曙光:Web Worker

單線程始終是一個痛點,爲了利用多核 CPU 的計算能力, HTML5 提出 Web Worker 標準,允許 JavaScript 腳本創建多個線程。但是子線程完全受主線程控制,且不得操作 DOM 。
所以,這個新標準並沒有改變 JavaScript 單線程的本質。
在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力
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 的黑魔法。

  1. postMessage 用來發送消息,而 onmessage 用來監聽消息
const worker = new Worker('src/worker.js');
worker.onmessage = e => {
  console.log(e.data);
};
worker.postMessage('你好嗎!');

在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力

在主線程中使用時, 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('謝謝支持');
  }
};在此我向大家推薦一個前端全棧開發交流圈:619586920  突破技術瓶頸,提升思維能力

代碼很簡單,主線程發送:「寫的真好!」
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('謝謝支持');
  }
};
在此我向大家推薦一個前端全棧開發交流圈:619586920  突破技術瓶頸,提升思維能力

這時候運行就會報出:


這是因爲: worker.js 執行的上下文,與主頁面 HTML 執行時的上下文並不相同,最頂層的對象並不是 Window , woker.js 執行的全局上下文,而是 WorkerGlobalScope ,我們具體說明。

  1. 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() 的。
在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力

爲了給 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 可以使用,這裏就不一一舉例了。
    在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力

八、異常處理

當 worker 出現運行中錯誤時,它的 onerror 事件處理函數會被調用。它會收到一個擴展了 ErrorEvent 接口的名爲 error 的事件。該事件不會冒泡並且可以被取消。
爲了防止觸發默認動作,worker 可以調用錯誤事件的 preventDefault() 方法。
錯誤事件我們常用如下這三個關鍵信息:
Message:可讀性良好的錯誤消息;
Filename:發生錯誤的腳本文件名;
Lineno:發生錯誤時所在腳本文件的行號;

worker.onerror = function(error) {
  console.log(error.message);
  throw error;
};

結語

感謝您的觀看,如有不足之處,歡迎批評指正。

獲取資料

本次給大家推薦一個免費的學習羣,裏面概括移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。
對web開發技術感興趣的同學,歡迎加入Q羣:619586920,不管你是小白還是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時每天更新視頻資料。
最後,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章