前端工程師們必定有過這樣的體驗,當一個頁面加載了大量的 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
咱們來看這樣一段代碼:瀏覽器
<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 的使用方法很簡單。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主線程:
worker新線程:
關於 Web Worker,最重要的是要知道它所執行的 Javascript 代碼徹底在另外一個做用域中,與當前網頁中的代碼不共享做用域。在 Web Worker 中,一樣有一個全局對象(worker 對象自己,this 和 self 引用的都是 worker 對象自己)和其餘對象以及方法。Web Worker 中的代碼不能訪問 DOM。那麼 Worker 裏的代碼能訪問哪些對象,擁有哪些方法?
任什麼時候候都能停止 Worker。在 worker.js 中,咱們能夠用 self.close()
方法,而在頁面中,咱們能夠用 worker.terminal()
方法,這時 error 和 message 事件也不會觸發了。