最近在项目中用到了多线程编程,所以就此处深入产开学习。
众所周知,javascript是单线程的语言,单线程意味着程序会按照指定的顺序按部就班的执行下去,遇到堵塞也无法越过。我们简单地看看下面这个例子:
function f1(){ console.time('time span'); } function f2(){ console.timeEnd('time span'); } setTimeout(f1,100); setTimeout(f2,200); function waitForMS(n){ var now = Date.now(); while(Date.now() - now < n){} } waitForMs(500);
上面的代码中,按字面上来理解,就是执行100ms后执行f1,执行200ms后执行f2。可是真正的结果是:
也可以理解为忽略不计。为什么会这样呢?前面我已经提到,javascript是单线程的,程序一开始的时候,waitForMs函数就占用了进程,导致f1和f2两个函数不能按照指定的时间间隔执行。
虽然js是单线程的,但是js运行的环境,也就是浏览器,是支持多线程的。所以我们可以通过很多方式来实现javascript的多线程编程。
第一种就是利用ajax技术,ajax确实是异步的,XMLHttpRequest请求是由浏览器开启一个线程来完成异步操作。当请求的状态变更时,如果先前已设置回调,那么异步线程就产生状态变更事件放到javascript引擎的事件处理队列中等待处理。当浏览器空闲的时候队列中任务被处理,javascript引擎始终是单线程运行回调函数。
第二种方式就是回调函数。回调函数也算是异步编程最基本的方式了。我们假设有两个函数,f1和f2.执行顺序为先执行f1,再执行f2。但是f1的执行时间有点长,我们希望可以在执行f1的同时也能够执行f2。
实现代码如下:
function f1(callback){ //f1的内容需要耗费很多时间 for(i=0;i<1000;i++){ console.log(i); } callback(); } function f2(){ alert(2); } f1(f2);输出结果就是在f2弹框的同时,f1中的循环也在执行。
采用这种方式,我们把同步操作变成了异步操作,f1不会阻塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作延迟执行。
回调函数的优点就是简单,容易理解和实现,缺点就是不利于代码的阅读和维护。比如以下代码:
function f1(f){ alert(1); f(); } function f2(f){ alert(2); f(); } function f3(){ alert(3); } f1( function(){f2(f3)} )
<pre name="code" class="html"> setTimeout(function () { that.$element.trigger(slidEvent) }, 0) })通过setTimeout将事件置于队列头部。
</pre><pre name="code" class="html">前面三种方式都是在过去被广泛运用的方式,随着HTML5的定稿,出现了一种新的概率。
<pre name="code" class="html"><h1>WebWorkers</h1>工作线程,也叫做WebWorkers,是HTML5中提出的对多线程的支持。Web Workers的三大主要特征:能够长时间运行,理想的启动性能以及理想的内存消耗。Web Workers允许开发人员编写能够长时间运行而不被用户所中断的后台程序,去执行事务或者逻辑,并同时保证页面对用户的及时响应。
Web Workers 为 Web 前端网页上的脚本提供了一种能在后台进程中运行的方法。一旦它被创建,Web Workers 就可以通过 postMessage 向任务池发送任务请求,执行完之后再通过 postMessage 返回消息给创建者指定的事件处理程序 ( 通过 onmessage 进行捕获 )。WebWorkers 进程能够在不影响用户界面的情况下处理任务,并且,它还可以使用 XMLHttpRequest 来处理 I/O,但通常,后台进程(包括 Web Workers 进程)不能对 DOM 进行操作。如果希望后台程序处理的结果能够改变 DOM,只能通过返回消息给创建者的回调函数进行处理。相信你已经急不可待了吧,马上上实例。首先,我们需要在客户端页面的javascript代码中new一个Worker实例出来,参数是需要在另一个线程运行的javascript文件名称。然后在这个实例上监听onmessage事件。主线程代码如下:
</pre><pre name="code" class="html"></pre><pre name="code" class="html"><!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body onload="init();"> <div id="result"></div> <script type="text/javascript"> function init(){ var worker=new Worker('compute.js');//实例化一个WebWorker,参数为另一个线程的js文件名称 worker.onmessage=function(event){//event中有data属性,就是子线程中返回的结果数据 //把子线程返回的结果添加到div中 document.getElementById("result").innerHTML+=event.data+"<br>"; } } </script> </body> </html>
var i=0; function timedCount(){ for(var j=0,sum=0;j<100;j++){ for(var i=1;i<10000000;i++){ sum+=i; } } postMessage(sum);//调用postMessage向主线程发送消息 } postMessage("开始计算 "+new Date()); timedCount(); postMessage("结束计算 "+new Date());
最后结果如下: