JavaScript多線程編程

瀏覽器端JavaScript是以單線程的方式執行的,也就是說JavaScript和UI渲染佔用同一個主線程,那就意味着,若是JavaScript進行高負載的數據處理,UI渲染就頗有可能被阻斷,瀏覽器就會出現卡頓,下降了用戶體驗。javascript

爲此,JavaScript提供了異步操做,好比定時器(setTimeout、setInterval)事件、Ajax請求、I/O回調等。咱們能夠把高負載的任務使用異步處理,它們將會被放入瀏覽器的事件任務隊列(event loop)中去,等到JavaScript運行時執行線程空閒時候,事件隊列纔會按照先進先出的原則被一一執行。html

nodejs引覺得榮的異步處理

經過相似定時器,回調函數等異步編程方式在日常的工做中已經足夠,可是若是作複雜運算,這種方式的不足就逐漸體現出來,好比settimeout拿到的值並不正確,或者頁面有複雜運算的時候很容易觸發假死狀態,異步代碼會影響主線程的代碼執行,異步終究仍是單線程,不能從根本上解決問題。前端

多線程(Web Worker)就應運而生,它是HTML5標準的一部分,這一規範定義了一套 API,容許一段JavaScript程序運行在主線程以外的另一個線程中。將一些任務分配給後者運行。在主線程運行的同時,Worker(子)線程在後臺運行,二者互不干擾。等到 Worker 線程完成計算任務,再把結果返回給主線程。這樣的好處是,一些計算密集型或高延遲的任務,被 Worker 線程負擔了,主線程(一般負責 UI 交互)就會很流暢,不會被阻塞或拖慢。java

1、什麼是web worker

worker是window對象的一個方法,能夠經過如下方式來檢測你的瀏覽器是否支持workernode

if (window.Worker) {…… your code ……}
複製代碼

一個worker是使用一個構造函數(Worker())建立的一個對象,這個構造函數須要傳入一個的JavaScript文件,這個文件包含將在工做線程中運行的代碼。相似於這樣:git

let myWorker = new Worker('worker.js');
複製代碼

worker經過postMessage() 方法和onmessage事件進行數據通訊。主線程和子線程是雙向的,均可以發送和監聽事件。向一個worker發送消息須要這樣作(main.js):github

myWorker.postMessage('hello, world'); // 發送
worker.onmessage = function (event) { // 接收
	console.log('Received message ' + event.data);
	doSomething();
}
複製代碼

postMessage所傳的數據都是拷貝傳遞(ArrayBuffer類型除外),數據子線程也是相似傳遞(worker.js)web

addEventListener('message', function (e) {
	postMessage('You said: ' + e.data);
}, false);
複製代碼

當子線程運行結束後,使用完畢,爲了節省系統資源,能夠手動關閉子線程。若是worker沒有監聽消息,那麼當全部任務執行完畢(包括計數器)後,它就會自動關閉。ajax

// 在主線程中關閉
worker.terminate();
// 在子線程裏線程
close();

// 監聽 error 事件
worker.addEventListener('error', function (e) {
  console.log('ERROR', e);
});
複製代碼

web worker自己很簡單,可是它的限制特別多。chrome

2、使用的問題

一、同源限制
分配給Worker 線程運行的腳本文件(worker.js),必須與主線程的腳本文件(main.js)同源。這裏的同源限制包括協議、域名和端口,不支持本地地址(file://)。這會帶來一個問題,咱們常用CDN來存儲js文件,主線程的worker.js的域名指的是html文件所在的域,經過new Worker(url)加載的url屬於CDN的域,會帶來跨域的問題,實際開發中咱們不會吧全部的代碼都放在一個文件中讓子線程加載,確定會選擇模塊化開發。經過工具或庫把代碼合併到一個文件中,而後把子線程的代碼生成一個文件url。
解決方法:
(1)將動態生成的腳本轉換成Blob對象。
(2)而後給這個Blob對象建立一個URL。
(3)最後將這個建立好的URL做爲地址傳給Worker的構造函數。

let script = 'console.log("hello world!");'
let workerBlob = new Blob([script], { type: "text/javascript" });
let url = URL.createObjectURL(workerBlob);
let worker = new Worker(url);
複製代碼

二、訪問限制
Worker子線程所在的全局對象,與主線程不在同一個上下文環境,沒法讀取主線程所在網頁的 DOM 對象,也沒法使用document、window、parent這些對象,global對象的指向有變動,window須要改寫成self,不能執行alert()方法和confirm()等方法,只能讀取部分navigator對象內的數據。另外chrome的console.log()卻是可使用,也支持debugger斷點,增長調試的便利性。
三、使用異步
Worker子線程中可使用XMLHttpRequest 對象發出 AJAX 請求,可使用setTimeout() setInterval()方法,也可以使用websocket進行持續連接。也能夠經過importScripts(url)加載另外的腳本文件,可是仍然不能跨域。

3、應用場景

一、使用專用線程進行數學運算
Web Worke設計的初衷就是用來作計算耗時任務,大數據的處理,而這種計算放在worker中並不會中斷前臺用戶的操做,避免代碼卡頓帶來沒必要要的用戶體驗。例如處理ajax返回的大批量數據,讀取用戶上傳文件,計算MD5,更改canvas的位圖的過濾,分析視頻和聲頻文件等。worker中除了缺失了DOM和BOM操做能力之外,仍是擁有很是強大的js邏輯運算處理的能力的,至關於nodejs一個級別的的運行環境。

二、高頻的用戶交互
高頻的用戶交互適用於根據用戶的輸入習慣、歷史記錄以及緩存等信息來協助用戶完成輸入的糾錯、校訂功能等相似場景,用戶頻繁輸入的響應處理一樣能夠考慮放在web worker中執行。例如,咱們能夠 作一個像Word同樣的應用:當用戶打字時後臺在詞典中進行查找,幫助用戶自動糾錯等等。

三、數據的預取
對於一些有大量數據的先後臺交互產品,能夠新開一個線程專門用來進行數據的預取和緩衝數據,本地web數據庫的行寫入和更改,長時間持續的運行,不會被主線程上的活動(好比用戶點擊按鈕、提交表單)打斷,也有利於隨時響應主線程的通訊。也能夠配合XMLHttpRequest和websocket進行不斷開的通訊,實現守衛進程。

4、兼容性

整體來講,兼容性仍是不錯的, 移動端能夠放心使用,桌面端要求不高的話,也可使用。

5、小結

對於web worker這項新技術,不管在PC仍是在移動web,騰訊新聞前端組進行了普遍的使用,Web Worker 的實現爲前端程序帶來了後臺計算的能力,能夠實現主 UI 線程與複雜計運算線程的分離,從而極大減輕了因計算量大而形成 UI 阻塞而出現的界面渲染卡、掉幀的狀況,而且更大程度地利用了終端硬件的性能。superWorker能解決掉事件綁定,同源策略的問題,它目前最大的問題在於不兼容IE9,在兼容性要求不是那麼嚴格的地方,儘量的使用吧!


《IVWEB 技術週刊》 震撼上線了,關注公衆號:IVWEB社區,每週定時推送優質文章。

相關文章
相關標籤/搜索