首页 > 代码库 > JS线程模型&Web Worker
JS线程模型&Web Worker
js线程模型
客户端javascript是单线程,浏览器无法同时运行两个事件处理程序
设计为单线程的理论是,客户端的javascript函数必须不能运行太长时间,否则会导致web浏览器无法对用户输入做出响应。这也是为什么Ajax的API都是异步的,以及为什么客户端Javascript不能使用一个简单的异步load()或者require()函数来加载javascript库
如果应用程序不得不执行太多的计算而导致明显的延迟,应该允许文档在执行这个计算之前完全载入,并且确保告诉用户正在进行计算并且浏览器没有挂起。如果可能应该将任务分散为离散的子任务,可以使用setTimeout()和setInterval()方法在后台运行子任务,同时更新一个进度指示器向用户显示反馈
Web worker简介
HTML5定义了一种作为后台线程的WebWorker。 web worker是一个用来执行计算密集任务而不冻结用户界面的后台线程。
Web worker无法访问window对象和document对象,和主线程之间的通信也只能通过异步消息传递机制来实现。
Web worker 本身不是轻量级的线程,因而常见一些worker去处理次要的操作是不划算的
浏览器支持情况:
包含两部分:
1. Worker对象:暴露给创建该线程的线程
2. WorkerGlobalScope:用来表示新创建的worker的全局对象
Web Worker基本使用
创建新的Worker:
var worker= new Worker("/assets/demo.js");
传递参数:
worker.postMessage("file.text");
接收消息:
worker.onmessage = function(e){ var message = e.data; ...... }
Worker当然也支持addEventListener()方法和removeEventListener()方法,如果需要管理多个事件时可以使用哒
异常处理:
worker.onerror = function(e){ console.log("Error at " + e.filename + ":" +e.lineno + e.message ); }
结束Worker
worker.terminate();
载入类和工具函数:
importScripts("utils/base64.js","utils/Map.js"....);
注意:importScripts是同步的方法,一旦importScripts方法返回就可以开始使用载入的脚本,不需要回调函数
Worker作用域
当创建一个新的Worker时该代码会运行在一个全新的Javascript运行环境中(WorkerGlobalScope),完全与创建Worker的脚本隔离
WorkerGlobalScope是Worker的全局对象,因而它包含所有核心Javascript全局对象拥有的属性如JSON等,window的一些属性如self等,也拥有类似XMLHttpRequest()函数
下面简单概括下worker所支持的属性和方法:
self
setTimeout、clearTimeout、setInterval、clearInterval
location
navigator
onerror
XMLHttpRequest
addEventListener、removeEventListener
简单例子
eg1:
html:
<div id="div"> <p> 计数: <output id="result"></output> </p> <button onclick="startWorker()">开始 Worker</button> <button onclick="stopWorker()">停止 Worker</button> <br /> <br /> <button onclick="mainWork()"> click me </button> <br /> <br /> </div>
js脚本:
var w; function startWorker() { if (typeof (Worker) !== "undefined") { if (typeof (w) == "undefined") { w = new Worker("demo.js"); } w.onmessage = function(event) { document.getElementById("result").innerHTML = event.data; }; } else { document.getElementById("result").innerHTML = "Sorry, your browser does not support Web Workers..."; } } function stopWorker() { w.terminate(); } function addE(){ var p = document.createElement(‘p‘); p.innerHTML="WISH YOU HAPPY~"; div.appendChild(p); }
demo.js:
/** * Web Worker Demo----count * */ var i=0; function timedCount() { i=i+1; postMessage(i); setTimeout("timedCount()",500); } timedCount();
结果:
点击开始Worker后,计数在后台进行,可以点击click me,相互不影响
eg2:
如上面所说,我们应该尽量使用WebWorker处理计算量大的,主要的工作,否则因为WebWorker本身不是轻量级的线程,因而有点得不偿失
本例使用WebWoker处理图片,将图片模糊,顺便学习下canvas,如下:
html:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <style> img { width: 400px; height: 300px; } </style> </head> <body> <img src="../images/demo.png" onclick="smear(this)"/> <img src="../images/1.png" onclick="smear(this)"/> <img src="../images/2.png" onclick="smear(this)"/> <script> function smear(img){ var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; var context = canvas.getContext("2d"); context.drawImage(img,0,0); var pixels = context.getImageData(0,0,img.width,img.height); var worker = new Worker("SmearWorker.js"); worker.postMessage(pixels); worker.onmessage = function(e){ var smeared_pixels = e.data; context.putImageData(smeared_pixels,0,0); img.src = canvas.toDataURL(); worker.terminate(); canvas.width = canvas.height = 0; } } </script> </body> </html>
SmearWorker.js:
/** * SmearWorker---smear the picture */ function smear(pixels){ var data =http://www.mamicode.com/ pixels.data, width = pixels.width, height = pixels.height; var n = 10, m = n-1, i, col; for(var row=0; row<height; row++){ i = row*width*4 + 4; for(col =1;col<width; col++,i+=4){ data[i] = (data[i] +data[i-4]*m/n); data[i+1] = (data[i+1] +data[i-3]*m/n); data[i+2] = (data[i+2] +data[i-2]*m/n); data[i+3] = (data[i+3] +data[i-1]*m/n); } } return pixels; } onmessage = function(e){ postMessage(smear(e.data)); };
结果如下:
图片点击前:
点击第一张和第三张图片:
其他
我们在js中使用XMLHttpRequest时经常会设置为异步方式,因而在主浏览器线程张使用同步很不好,我们可以在worker中使用同步的XMLHttpRequest