javascript多线程

来源:互联网 发布:简单的c语言程序文件 编辑:程序博客网 时间:2024/05/20 00:38

最近在项目中用到了多线程编程,所以就此处深入产开学习。

众所周知,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技术,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)}  )

如果逻辑很复杂,就会形成传说中的回调地狱。

setTimeout

第三种办法就是利用setTimeout来模拟开启一个线程,其实并没有开启线程。javascript中定时器的原理还是有点复杂的。简单的可以理解为,setTimeout会将事件放入主程序的事件队列,如果主程序空闲了,就会第一时间调用setTimeout中的事件。
这种方式用得比较广泛,很多开源框架源码中都随处可见,比如bootstrap中:
<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>

子线程通过postMessage方法就可以在两个线程间传递数据了。子线程compute.js代码如下:

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());
最后结果如下:


至于WebWorker的深入理解可以看:这篇文章






1 0