Web Worker 是什麼鬼?

前言


前端工程師們必定有過這樣的體驗,當一個頁面加載了大量的 js 文件時,用戶界面可能會短暫地「凍結」。這很好理解,由於 js 是單線程的語言。咱們再走的極端點,一段 js 中出現了 while(){} 的死循環,這時再去點擊頁面的 DOM 元素,將不會觸發事件,事實上,這些異步的事件都排成了隊列,只等頁面的 js 渲染完後去執行(從setTimeout談JavaScript運行機制),而此時渲染進入了死循環,因此出現了用戶界面被「凍結」的現象。html

而實際的開發中,雖然不會出現相似的死循環,可是大量的 js 渲染仍是會影響用戶體驗的,此時咱們但願這段耗時的 js 最好能異步去執行,setTimeout 是一個好的方法,可是 H5 提供了更好的辦法,Web Worker! Web Worker 規範經過讓 Javascript 在後臺運行解決了這個問題。瀏覽器實現 Web Worker 規範的方式有不少種,可使用線程、後臺進程或者運行在其餘處理器核心上的進程,等等。具體的實現細節其實沒有那麼重要,重要的是開發人員如今能夠放心地運行 Javascript,而沒必要擔憂會影響用戶體驗了。前端

既然 Worker 是 H5 你們庭的,那麼 ie6 之輩就能夠一邊去了,詳細的瀏覽器兼容性能夠參考 http://caniuse.com/#search=workerajax

若是沒有 Worker


咱們來看這樣一段代碼:瀏覽器

<div style='width:100px;height:100px;background-color:red'></div>
<script>
  document.querySelector('div').onclick = function() {
    console.log('hello world');
  };

  function fibonacci(n) {
    return n < 2 ? n : arguments.callee(n - 1) + arguments.callee(n - 2);
  }

  console.log(fibonacci(36));
</script>

頁面上寫了一個 div,而後監聽了它的 click 事件,而且在 js 中須要計算斐波那契數列的第 36 項,並將它輸出。這樣的頁面用戶體驗是很是差的,若是 fibonacci 不執行完,div 的 click 事件是沒法及時響應的,而遞歸求解斐波那契數列項是至關耗時的!這樣一段耗時的 js 代碼,交給 worker 來作正合適!前端工程師

祭出大殺器 Worker


Worker 的使用方法很簡單。app

實例化 Worker 對象並傳入要執行的 Javascript 文件名就能夠建立一個新的 Web Worker:異步

var worker = new Worker('worker.js');

這段代碼會致使瀏覽器下載 worker.js,但只有 Worker 接收到消息纔會實際執行文件中的代碼。要給 Worker 傳遞消息,可使用 postMessage() 方法,好比我要告訴 Worker 須要求斐波那契數列的第 36 項:函數

worker.postMessage(36);

咱們來看看 Worker 是怎麼接收消息的。當頁面在 worker 對象上調用 postMessage()時,數據會以異步方式傳遞給 worker,進而觸發 worker 中的 message 事件。爲了處理來自頁面的數據,一樣也要建立一個 onmessage 事件處理程序。(worker.js 代碼)post

self.onmessage = function(event) {
  var data = event.data;
  console.log(fibonacci(data));
};

function fibonacci(n) {
  return n < 2 ? n : arguments.callee(n - 1) + arguments.callee(n - 2);
}

頁面能夠傳數據給 Worker,Worker 固然也能夠回傳,頁面經過 message 事件進行監聽:this

worker.onmessage = function(event) {
  var data = event.data;
};

Worker 是經過 message 和 error 事件與頁面通訊的。來自 Worker 的數據保存在 event.data 中。Worker 不能完成給定的任務時會觸發 error 事件。具體來講,Worker 內部的 Javascript 在執行過程當中只要遇到錯誤,就會觸發 error 事件。發生 error 事件時,事件對象中包含三個屬性:filename、lineno 和 message,分別表示錯誤的文件名、代碼行號和完整的錯誤信息:

worker.onerror = function(event) {
  console.log(event.filename, event.lineno, event.message);
};

咱們建議使用 worker 時最好寫上 error 事件,就像使用 ajax 時總要寫上獲取失敗時的補救操做同樣。

完整代碼(運行 demo 必須起個本地服務):

html 文件:

<div style='width:100px;height:100px;background-color:red'></div>
<script>
  document.querySelector('div').onclick = function() {
    console.log('hello world');
  };

  var worker = new Worker('worker.js');
  worker.postMessage(36);
  worker.onmessage = function(event) {
    var data = event.data;
    console.log(data)
  };

  worker.onerror = function(event) {
    console.log(event.filename, event.lineno, event.message);
  };
</script>

worker.js 文件:

self.onmessage = function(event) {
  var data = event.data;
  var ans = fibonacci(data);
  this.postMessage(ans);
};

function fibonacci(n) {
  return n < 2 ? n : arguments.callee(n - 1) + arguments.callee(n - 2);
}

簡單小結:

WEB主線程:

  1. 經過 var worker = new Worker(url) 加載一個 js 文件來建立一個 worker,同時返回一個 worker 實例。
  2. 經過 worker.postMessage(data) 方法來向 worker 發送數據。
  3. 綁定 worker.onmessage 方法來接收 worker 發送過來的數據。
  4. 可使用 worker.terminate() 來終止一個 worker 的執行。

worker新線程:

  1. 綁定 onmessage 方法來接收主線程發送過來的數據。
  2. 經過 postMessage(data) 方法來向主線程發送數據。
  3. 可使用 self.close() 來終止一個 worker 的執行。

Worker 其餘


關於 Web Worker,最重要的是要知道它所執行的 Javascript 代碼徹底在另外一個做用域中,與當前網頁中的代碼不共享做用域。在 Web Worker 中,一樣有一個全局對象(worker 對象自己,this 和 self 引用的都是 worker 對象自己)和其餘對象以及方法。Web Worker 中的代碼不能訪問 DOM。那麼 Worker 裏的代碼能訪問哪些對象,擁有哪些方法?

  1. 最小化 的navigator 對象,包括 onLine、appName、appVersion、userAgent 和 platform 屬性
  2. 只讀的 location 對象
  3. setTimeout()、setInterval()、clearTimeout()、clearInterval() 方法
  4. XMLHttpRequest 構造函數

任什麼時候候都能停止 Worker。在 worker.js 中,咱們能夠用 self.close()方法,而在頁面中,咱們能夠用 worker.terminal()方法,這時 error 和 message 事件也不會觸發了。

相關文章
相關標籤/搜索