簡(jiǎn)單背景介紹
JavaScripts 本身是一種單線程設(shè)計(jì),無(wú)法在同一時(shí)刻并行的運(yùn)行多個(gè)腳本。瀏覽器處理的每一個(gè)任務(wù)都是通過(guò)串行的方式進(jìn)行處理。
通常當(dāng)使用setTimeout()和setInterval()這樣的函數(shù)時(shí),可能會(huì)產(chǎn)生多個(gè)線程獨(dú)立于JavaScript主線程同時(shí)運(yùn)行的錯(cuò)覺(jué),但事實(shí)上,這些函數(shù)其實(shí)都被加入了主線程使用得同一個(gè)事件循環(huán)里。這種方式有一個(gè)缺點(diǎn),一旦使用任何會(huì)導(dǎo)致阻塞的函數(shù),就會(huì)使瀏覽器失去響應(yīng)。
如Ajax中使用的XMLHttpRequest對(duì)象有同步和異步兩種模式。異步模式使用的頻率要遠(yuǎn)遠(yuǎn)高于同步模式,因?yàn)橥降恼?qǐng)求會(huì)牽制整個(gè)線程,在請(qǐng)求返回前會(huì)阻塞所有后續(xù)指令的執(zhí)行。這會(huì)導(dǎo)致所有與頁(yè)面的交互行為全部失效,表現(xiàn)出來(lái)的效果就是頁(yè)面雖然顯示出來(lái)了,但是會(huì)沒(méi)有反應(yīng)。
Web Workers
Web Workers通過(guò)引入類似線程的機(jī)制是這種問(wèn)題得到有效的解決。
使用Web Workers創(chuàng)建一個(gè)工作線程就是能夠簡(jiǎn)單地加載一段腳本并在后臺(tái)線程中執(zhí)行。 一個(gè)運(yùn)行在工作線程中的腳本是無(wú)法影響或阻塞主線程的,這意味著可以一邊進(jìn)行cpu密集型的處理,同時(shí)用戶繼續(xù)運(yùn)行游戲或者應(yīng)用。
分類
專用線程
- 專用型worker與創(chuàng)建它的腳本連接在一起,它可以與其他的worker或是瀏覽器組件通信,但是他不能與DOM通信。專用的含義,就是這個(gè)線程一次只處理一個(gè)需求。專用線程在除了IE外的各種主流瀏覽器中都實(shí)現(xiàn)了,可以放心使用。
- 專用線程只適合當(dāng)前客戶端使用,與其他客戶端無(wú)關(guān)聯(lián),適用于本客戶端內(nèi)(本瀏覽器)的多線程使用。
共享線程
- 共享線程適用于多個(gè)客戶端(多個(gè)瀏覽器)之間進(jìn)行數(shù)據(jù)交互和控制,但是html5中沒(méi)有類似鎖機(jī)制,所以安全性存在一定問(wèn)題。
- 共享線程可以有多個(gè)連接。用于解決多連接并發(fā)的問(wèn)題。它們并不是綁定于一個(gè)HTML頁(yè)面的。如果你在同一個(gè)瀏覽器上打開(kāi)了同一個(gè)網(wǎng)站的頁(yè)面,這些頁(yè)面都可以訪問(wèn)其中任意頁(yè)面創(chuàng)建的共享工作線程。
創(chuàng)建專用線程
- 創(chuàng)建一個(gè)工作線程需要使用Worker( )構(gòu)造函數(shù),把需要在線程中執(zhí)行的JavaScript文件的文件名傳給構(gòu)造函數(shù)就可以了。
var worker = new Worker("myworker.js");
- 然后在實(shí)例上監(jiān)聽(tīng)onmessage事件,來(lái)獲取消息。
worker.onmessage = function(event){
//從工作線程獲取消息
}
- 或者使用監(jiān)聽(tīng)方法
worker.addEventListener("message",function(event){
//從工作線程獲取消息
},false);
- 兩種方式下,都可以在event對(duì)象的data屬性中找到消息數(shù)據(jù)。數(shù)據(jù)已經(jīng)自動(dòng)地由JSON格式解碼為原始格式,因此數(shù)據(jù)結(jié)構(gòu)沒(méi)有任何改變。
- 最后通過(guò)調(diào)用postMessage( )函數(shù)在不同線程中傳遞數(shù)據(jù)。
工作線程和它們的父線程通過(guò)一組公共的消息API進(jìn)行通信。數(shù)據(jù)都是通過(guò)字符串的形式進(jìn)行傳遞的,但是這并不意味著你只能發(fā)送字符串形式的而消息。如果你發(fā)送一些復(fù)雜的結(jié)構(gòu)體,比如對(duì)象或者數(shù)組,它們將自動(dòng)轉(zhuǎn)換成JSON格式。但是DOM元素是不能轉(zhuǎn)換成JSON的,因此其不能與DOM通信。
- 當(dāng)你使用工作線程完成了既定的工作時(shí),需要銷(xiāo)毀線程。
在線程內(nèi)部,使用close方法線程自己銷(xiāo)毀自己。在線程外部的主線程中,使用線程實(shí)例的terminate方法銷(xiāo)毀線程。
worker.terminate( );
- 使用其他腳本
工作線程可以使用全局方法importScripts來(lái)加載和使用其他的域內(nèi)腳本文件或者類庫(kù)。如:
importScripts(); importScripts('foo.js'); importScripts('foo.js', 'bar.js'); - 線程嵌套
在工作線程中還可以在創(chuàng)建子線程。
- 同步問(wèn)題
Worker沒(méi)有鎖的機(jī)制,多線程的同步問(wèn)題只能靠代碼來(lái)解決(比如定義信號(hào)變量)。
共享線程與專用線程
共享線程可以有多個(gè)連接。用于解決多連接并發(fā)的問(wèn)題。它們并不是綁定于一個(gè)HTML頁(yè)面的。如果你在同一個(gè)瀏覽器上打開(kāi)了同一個(gè)網(wǎng)站的頁(yè)面,這些頁(yè)面都可以訪問(wèn)其中任意頁(yè)面創(chuàng)建的共享工作線程。
- 可以使用 SharedWorker()構(gòu)造函數(shù),創(chuàng)建共享工作線程。
除了腳本的路徑外,這個(gè)構(gòu)造函數(shù)還需要一個(gè)可選的name參數(shù)。如果name參數(shù)沒(méi)有指定會(huì)使用一個(gè)空字符串。如果創(chuàng)建一個(gè)和既有實(shí)例使用相同腳本和名字的共享工作線程,只會(huì)為已存在的線程增加一個(gè)新的連接而并不會(huì)創(chuàng)建一個(gè)全新的線程。
- 共享線程不像專用工作線程那樣擁有一個(gè)全局的message事件。他們必須來(lái)監(jiān)聽(tīng)connect事件來(lái)獲取新頁(yè)面何時(shí)向共享工作線程創(chuàng)建創(chuàng)建連接。工作線程和創(chuàng)建連接那個(gè)線程之間的通信是基于觸發(fā)message事件的port對(duì)象,利用的是其提供的postMessage( )。必須要調(diào)用port.start( )才能開(kāi)始接收消息。
Web Worker 使用注意:
- 同源限制
分配給 Worker 線程運(yùn)行的腳本文件,必須與主線程的腳本文件同源。
- DOM 限制
Worker 線程所在的全局對(duì)象,與主線程不一樣,無(wú)法讀取主線程所在網(wǎng)頁(yè)的 DOM 對(duì)象,也無(wú)法使用document、window、parent這些對(duì)象。但是,Worker 線程可以navigator對(duì)象和location對(duì)象。
- 通信聯(lián)系
Worker 線程和主線程不在同一個(gè)上下文環(huán)境,它們不能直接通信,必須通過(guò)消息完成。
- 腳本限制
Worker 線程不能執(zhí)行alert()方法和confirm()方法,但可以使XMLHttpRequest 對(duì)象發(fā)出 AJAX 請(qǐng)求。
- 文件限制
Worker 線程無(wú)法讀取本地文件,即不能打開(kāi)本機(jī)的文件系統(tǒng)(file://),它所加載的腳本,必須來(lái)自網(wǎng)絡(luò)。
Web Worker的兼容性

從圖上看兼容性異常的好,甚至連IE系列都在好幾年前就已經(jīng)支持,但是這個(gè)兼容性只能說(shuō)明能否使用Web Woker,這里的兼容并不能表明能在其中做其他操作。比如標(biāo)準(zhǔn)規(guī)定,可以在子線程做做計(jì)算、發(fā)起XHR請(qǐng)求等,但不能操作DOM對(duì)象,但實(shí)際使用中,F(xiàn)ecth在IE系列(包括Edge)瀏覽器中并不支持,會(huì)直接報(bào)錯(cuò)。