前言:一直都以为只有java才能谈多线程这么高逼格的话题,惋惜java和javascript的关系就比如雷锋跟雷峰塔的关系,不过在HTML5新特性中有一个webworker,关于它的理论若是没有相关demo去实践它可能理解起来会有点僵硬。本文是经过canvas里面的一个操做像素的demo来更为直观地展示webworker的性能。javascript
JavaScript引擎是单线程运行的,JavaScript中耗时的I/O操做都被处理为异步操做,它们包括键盘、鼠标I/O输入输出事件、窗口大小的resize事件、定时器(setTimeout、setInterval)事件、Ajax请求网络I/O回调等。当这些异步任务发生的时候,它们将会被放入浏览器的事件任务队列中去,等到JavaScript运行时执行线程空闲时候才会按照队列先进先出的原则被一一执行,但终究仍是单线程。只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),每每就是由于某一段Javascript代码长时间运行(好比运行一个运算量很是大的函数),致使整个页面卡在这个地方,其余任务没法执行。java
正是由于单线程因此注定了js中的某些操做必须是异步的,举个粒子:node
window.onload = function () {
console.log(111);
setTimeout(function() {
console.log(222);
}, 0);
console.log(333);
}复制代码
上面的代码依次输出111,333,222,由于运行js碰到属于异步操做的代码就会单独拎出来放在一个队列(setTimeout就属于异步操做,不论是不是0秒后执行),等待同步代码(不属于异步操做的代码就是同步了)执行完就依次执行队列里的操做。其实在head里面加载js的话window.onload自己也属于异步代码。git
一、利用setTimout或回调实现异步es6
二、动态建立script标签github
三、利用script提供的defer/asyncweb
四、es6里的promiseajax
这里只是总结一下,以为前三种都很鸡肋,用好回调和promise大法就行了。编程
Web Worker 是HTML5标准的一部分,这一规范定义了一套API,它容许一段JavaScript程序运行在主线程以外的另一个线程中。工做线程容许开发人员编写可以长时间运行而不被用户所中断的后台程序, 去执行事务或者逻辑,并同时保证页面对用户的及时响应,能够将一些大量计算的代码交给web worker运行而不冻结用户界面。这是这个标题几乎标准的答案,好像仍是能够理解的。canvas
//---------------放在页面上的main.js:主线程
var worker = new Worker("worker.js");
// 主线程向子线程发送数据
worker.postMessage(data);
worker.onmessage = function(ev){
var data = ev.data;
// 主线程收到子线程的处理后的数据
};
//-----------------------------------//
//-------------放在后台的worker.js:子线程
self.onmessage = function(ev){
var data = ev.data //收到主线程发过来的源数据
var newdata = handle(data);
self.postMessage(newdata );//把处理好的数据发回去
};
//把一些运算不叫复杂或工做量大的js代码拎过来
function handle(data){ some coding... }
//----------------------------------//
//-----------------------------中止worker
// 方式一 main.js 在主线程中止方式
var worker = new Worker('./worker.js');
...
worker.terminate();
// 方式2、worker.js
self.close();复制代码
这里就先罗列一个最基本的流程语法吧,很是简单,须要特别注意:子线程里面的js代码所支持的语法很是有限,只支持ECMAscript的基本语法,具体范围是多大呢,咱们都知道javascript大体分为ECMAscript、DOM、BOM、nodeJs。第一类是基础,DOM类是基于ECMAscript实现的去操做DOM树的,BOM类就是浏览器行为语法,而nodeJs则是基于ECMAscript去操做os、file、database、net等等之类的(不知道这样理解会不会被人打^_^)。因此它只支持像string、array、object...这样子的东西,连console.log和alert都不支持,相信你们确定知道这个范围。还有上面说了线程是后台开的,这些文件天然要放在服务器环境下运行的。在子线程中还能够经过 importScripts( 'another1.js' ,'another2.js' )来引入其余的js文件,是否是看到一点原生js模块化的影子呢,然而这其实没什么软用。
//------------------------------------------------主线程
window.onload = function () {
var oc = document.getElementById("print");
var ogc = oc.getContext('2d');
ogc.fillStyle = "#fc6423";
var ali = document.getElementsByTagName('li');
for (var i = 0; i < ali.length; i++) {
ali[i].onclick = function () {
console.time(1);//------------------计时开始
ogc.clearRect(0, 0, oc.width, oc.height);
ogc.save();
var h = 200;
var str = this.innerHTML;
ogc.font = h * 0.85 + 'px impact';
ogc.textBaseline = 'top';
var w = ogc.measureText(str).width;
ogc.fillText(str, (oc.width - w) / 2, (oc.height - h) / 2);
var allarea = ogc.getImageData((oc.width - w) / 2, (oc.height - h) / 2, w, h);
console.log(w);
ogc.clearRect(0, 0, oc.width, oc.height);
var newarea = ogc.createImageData(w, h);
// var allarr = suiji(w * h, w * h / 10);
//这里开一个worker,为的就是把上一行代码:10组随机数的产生拎到子线程中去运行
var worker = new Worker('cutarr.js');
worker.postMessage(w*h);// 将数据发给子线程
worker.onmessage = function (ev) {
var allarr = ev.data;//收处处理后的数据
var inow = 0;
var timer = null;
timer = setInterval(function () {
for (var i = 0; i < allarr[inow].length; i++) {
newarea.data[allarr[inow][i] * 4] = allarea.data[allarr[inow][i] * 4];
newarea.data[allarr[inow][i] * 4 + 1] = allarea.data[allarr[inow][i] * 4 + 1];
newarea.data[allarr[inow][i] * 4 + 2] = allarea.data[allarr[inow][i] * 4 + 2];
newarea.data[allarr[inow][i] * 4 + 3] = allarea.data[allarr[inow][i] * 4 + 3];
}
ogc.putImageData(newarea, (oc.width - w) / 2, (oc.height - h) / 2);
if (inow == 9) {
inow = 0;
clearInterval(timer);
}
inow++;
}, 150)
ogc.restore();
}
console.timeEnd(1); //------------------计时结束
}
}
}
//--------------------------------------------------子线程
function suiji(all, part) {
var arr1 = [];
var allarr = [];
for (var i = 0; i < all; i++) {
arr1.push(i);
}
for (var i = 0; i < all / part; i++) {
var newarr = [];
for (var j = 0; j < part; j++) {
newarr.push(arr1.splice(Math.floor(Math.random() * arr1.length), 1));
}
allarr.push(newarr);
}
return allarr;
}
self.onmessage = function (ev) {
var arr = suiji(ev.data,ev.data/10);
self.postMessage(arr);
}复制代码
上面这段js代码要完成的是将一块区域内的近10万颗像素(可调文字大小改变)每隔150ms随机显示其1/10的像素点,最终造成一个完整的汉字,其中最耗时的过程在于随机数的产生,有几个像素点就进行几回random操做。这是由随机函数suiji来完成,这里单纯地把它拎到子线程中去处理。上面的代码是用了webworker的,不使用webworker:即把suiji()放在window.onload里面,直接用 var allarr = suiji(w h, w h / 10)来产生就行了。接下来对比一下两种请况下从点击每一个字到打印计时一共花费多少时间:
一个接近2秒,一个10ms之内,这。。。并且前面提到:工做线程容许开发人员编写可以长时间运行而不被用户所中断的后台程序, 去执行事务或者逻辑,并同时保证页面对用户的及时响应,也就是说,当浏览器碰到这句代码:
var allarr = suiji(w * h, w * h / 10);复制代码
页面就会卡死近2000ms,不能有任何操做,好比说点击、右键,更不用说执行后面的代码了:
console.log(111);
var allarr = suiji( w*h,w*h/10 );
console.log(222);复制代码
这样我看到:几乎在点击的同时就打印了111,而22二、计时和分割线是在近2000ms以后同时打印的,由于上面三行代码都是同步的,而用了webworker则不存在卡死的请况,速度也快多了。若是面积小看不出有多少差异,能够若是把大小调到500,前者就达到了恐怖的15秒之多了。但是这时候用了webworker虽然不会形成卡死状态,但好像也快不了多少,也许在一个可控的范围内才能发挥它的性能吧,或者说原本js代码对多线程的支持就很差。你们能够本身去编写更复杂变态的函数去测验。
Web Worker带来后台计算能力,WebWorker自身是由webkit多线程实现,但它并无为Javasctipt语言带来多线程编程特性,咱们如今仍然不能在Javascript代码中建立并管理一个线程,或者主动控制线程间的同步与锁等特性。Web Worker 只是浏览器(宿主环境)提供的一个能力/API。并且它不支持IE。
一、使用专用线程进行数学运算
Web Worker最简单的应用就是用来作后台计算,而这种计算并不会中断前台用户的操做
二、 图像处理
经过使用从canvas或者video元素中获取的数据,能够把图像分割成几个不一样的区域而且把它们推送给并行的不一样Workers来作计算
三、大量数据的检索
当须要在调用 ajax后处理大量的数据,若是处理这些数据所需的时间长短很是重要,能够在Web Worker中来作这些,避免冻结UI线程。
四、背景数据分析
因为在使用Web Worker的时候,咱们有更多潜在的CPU可用时间,咱们如今能够考虑一下JavaScript中的新应用场景。咱们如今能够考虑一下JavaScript中的新应用场景。例如,咱们能够想像在不影响UI体验的状况下实时处理用户输入。利用这样一种可能,咱们能够想像一个像Word(Office Web Apps 套装)同样的应用:当用户打字时后台在词典中进行查找,帮助用户自动纠错等等。