HTML5 Web Workers能夠使得你的Web應用程序具有後臺處理能力,可以支持多線程,且能夠充分利用多核CPU帶來的優點。以免JavaScript循環持續運行後彈出的警告窗口。css
儘管Web Workers功能強大,可是也存在一些缺點,好比不能直接訪問Web頁面和DOM API、消耗CPU週期且致使系統反應速度變慢。html
若是須要Web應用執行一些後臺數據處理,但又不影響Web頁面自己的交互時,就能夠使用Web Workers,同時須要添加一個監聽器以監聽它發出的消息。git
1、HTML5 Web Workers API概述canvas
一、瀏覽器支持檢查瀏覽器
typeof(Worker) !== "undefined"
二、建立HTML5 Web Workers
多線程
Web Workers初始化時會接受一個JavaScript文件的URL地址(URL能夠是絕對或者相對路徑,但必須同源),其中包含了供Worker執行的代碼。這段代碼會設置事件監聽器,並與生成Worker的容器進行通訊。併發
worker = new Worker("echoWorker.js");
三、多個JavaScript文件的加載與執行
app
因爲Web Workers沒有訪問document對象的權限,所以不能使用常規導入JavaScript的方式,須要另外一種方法來導入JavaScript,即importScripts:函數
importScripts("helper.js","example.js","blur.js");
多個腳本的導入會按照導入順序執行。
post
四、與HTML5 Web Workers通訊
Web Workers一旦生成,就能夠使用postMessage API傳送和接收數據。
爲了能與Web Workers成功通訊,除了在主頁(調用Web Workers的頁面)中添加代碼外,Worker JavaScript文件中也要添加相應代碼。
2、Web Workers示例應用
一、創建主頁與Web Workers之間的通訊
首先要添加對postMessage函數的調用,以下所示:
worker.postMessage("發送示例消息");
相應消息發送給Web Workers後,還須要添加監聽器,用以監聽Web Workers發來的消息
worker.addEventListener("message",messageHandler,true); function messageHandler(e){ //處理worker發來的消息 }
在Web Workers JavaScript文件中,也須要添加上述相似的代碼:必須添加事件監聽器來監聽發來的消息和錯誤消息,而且經過用postMessage函數實現與頁面之間的通訊。
二、處理錯誤
監聽到錯誤事件發生後,要將出錯信息反饋給用戶:
function errorHandler(e){ console.log(e.message,e); }
三、終止Web Workers
worker.terminate();
終止以後的Web Workers不能被從新啓動,但能夠使用一樣的URL建立一個新的Worker。
四、示例代碼整合
本示例實現 一個帶有圖像模糊過濾器的Web頁面,其中過濾動做將由多個Web Worker併發執行。
(1)編寫blur.js輔助腳本
function inRange(i, width, height) { return ((i>=0) && (i < width*height*4)); } function averageNeighbors(imageData, width, height, i) { var v = imageData[i]; // cardinal directions var north = inRange(i-width*4, width, height) ? imageData[i-width*4] : v; var south = inRange(i+width*4, width, height) ? imageData[i+width*4] : v; var west = inRange(i-4, width, height) ? imageData[i-4] : v; var east = inRange(i+4, width, height) ? imageData[i+4] : v; // diagonal neighbors var ne = inRange(i-width*4+4, width, height) ? imageData[i-width*4+4] : v; var nw = inRange(i-width*4-4, width, height) ? imageData[i-width*4-4] : v; var se = inRange(i+width*4+4, width, height) ? imageData[i+width*4+4] : v; var sw = inRange(i+width*4-4, width, height) ? imageData[i+width*4-4] : v; // average var newVal = Math.floor((north + south + east + west + se + sw + ne + nw + v)/9); if (isNaN(newVal)) { sendStatus("bad value " + i + " for height " + height); throw new Error("NaN"); } return newVal; } function boxBlur(imageData, width, height) { var data = []; var val = 0; for (var i=0; i<width*height*4; i++) { val = averageNeighbors(imageData, width, height, i); data[i] = val; } return data; }
(2)編寫blur.html頁面
<!DOCTYPE html> <title>HTML5 Web Workers</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href = "styles.css"> <h1>HTML5 Web Workers</h1> <p id="status">你的瀏覽器不支持HTML5 Web Workers.</p> <button id="startBlurButton" disabled>模糊</button> <button id="stopButton" disabled>中止Workers</button> <button onclick="document.location = document.location;">從新加載</button> <label for="workerCount">Workers數量</label> <select id="workerCount"> <option>1</option> <option selected>2</option> <option>4</option> <option>8</option> <option>16</option> </select> <div id="imageContainer"></div> <div id="logOutput"></div>
(3)編寫blurWorker.js
importScripts("blur.js"); function sendStatus(statusText){ postMessage({"type" : "status", "statusText" : statusText}); } function messageHandler(e){ var messageType = e.data.type; switch(messageType){ case ("blur"): sendStatus("Worker開始模糊的數據範圍: " + e.data.startX + "-" + (e.data.startX+e.data.width)); var imageData = e.data.imageData; imageData = boxBlur(imageData, e.data.width, e.data.height, e.data.startX); postMessage({"type" : "處理", "imageData" : imageData, "width" : e.data.width, "height" : e.data.height, "startX" : e.data.startX }); sendStatus("在數據範圍: " + e.data.startX + "-" + (e.data.width+e.data.startX) + "完成數據模糊"); break; default: sendStatus("Worker得到消息:" + e.data); } } addEventListener("message", messageHandler, true);
(4)與Web Workers進行通訊
function sendBlurTask(worker, i, chunkWidth) { var chunkHeight = image.height; var chunkStartX = i * chunkWidth; var chunkStartY = 0; var data = ctx.getImageData(chunkStartX, chunkStartY, chunkWidth, chunkHeight).data; worker.postMessage({'type' : 'blur', 'imageData' : data, 'width' : chunkWidth, 'height' : chunkHeight, 'startX' : chunkStartX}); }
(5)示例代碼
<!DOCTYPE html> <title>HTML5 Web Workers</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href = "styles.css"> <h1>HTML5 Web Workers</h1> <p id="status">你的瀏覽器不支持HTML5 Web Workers.</p> <button id="startBlurButton" disabled>模糊</button> <button id="stopButton" disabled>中止Workers</button> <button onclick="document.location = document.location;">從新加載</button> <label for="workerCount">Workers數量</label> <select id="workerCount"> <option>1</option> <option selected>2</option> <option>4</option> <option>8</option> <option>16</option> </select> <div id="imageContainer"></div> <div id="logOutput"></div> <script> var imageURL = "example2.png"; var image; var ctx; var workers = []; window.addEventListener("load", loadDemo, true); function loadDemo(){ log("加載圖像數據"); if(typeof(Worker) !== "undefined"){ document.getElementById("status").innerHTML = "你的瀏覽器支持HTML5 Web Workers"; document.getElementById("stopButton").onclick = stopBlur; document.getElementById("startBlurButton").onclick = startBlur; loadImageData(imageURL); document.getElementById("startBlurButton").disabled = true; document.getElementById("stopButton").disabled = true; } } function log(s){ var logOutput = document.getElementById("logOutput"); logOutput.innerHTML = s + "<br>" + logOutput.innerHTML; } function loadImageData(url){ var canvas = document.createElement('canvas'); ctx = canvas.getContext('2d'); image = new Image(); image.src = url; document.getElementById("imageContainer").appendChild(canvas); image.onload = function(){ canvas.width = image.width; canvas.height = image.height; ctx.drawImage(image, 0, 0); window.imgdata = ctx.getImageData(0, 0, image.width, image.height); n = ctx.createImageData(image.width, image.height); setRunningState(false); log("圖像加載:" + image.width + "x" + image.height + "pixels"); } } function setRunningState(p){ document.getElementById("startBlurButton").disabled = p; document.getElementById("stopButton").disabled = !p; } function stopBlur(){ for(var i=0; i<workers.length; i++){ workers[i].terminate(); } setRunningState(false); } function startBlur(){ var workerCount = parseInt(document.getElementById("workerCount").value); var width = image.width/workerCount; for(var i=0; i<workerCount; i++){ var worker = initWorker("blurWorker.js"); worker.index = i; worker.width = width; workers[i] = worker; sendBlurTask(worker, i, width); } setRunningState(true); } function initWorker(src){ var worker = new Worker(src); worker.addEventListener("message",messageHandler, true); worker.addEventListener("error",errorHandler, true); return worker; } function messageHandler(e) { var messageType = e.data.type; switch (messageType) { case ("status"): log(e.data.statusText); break; case ("progress"): var imageData = ctx.createImageData(e.data.width, e.data.height); for (var i = 0; i<imageData.data.length; i++) { var val = e.data.imageData[i]; if (val === null || val > 255 || val < 0) { log("illegal value: " + val + " at " + i); return; } imageData.data[i] = val; } ctx.putImageData(imageData, e.data.startX, 0); // blur the same tile again sendBlurTask(e.target, e.target.index, e.target.width); break; default: break; } } function errorHandler(e) { log("error: " + e.message); } function sendBlurTask(worker, i, chunkWidth) { var chunkHeight = image.height; var chunkStartX = i * chunkWidth; var chunkStartY = 0; var data = ctx.getImageData(chunkStartX, chunkStartY, chunkWidth, chunkHeight).data; worker.postMessage({'type' : 'blur', 'imageData' : data, 'width' : chunkWidth, 'height' : chunkHeight, 'startX' : chunkStartX}); } </script>
源碼請到http://git.oschina.net/niweiwei/HTML5/tree/master/workers查看