HTML5中我觉得最有用和激动人心的功能就是引入了线程的概念,从而我们可以用多线程的思想来处理比较复杂的应用。我们可以让前台线程去完成和用户交互的工作,而把比较复杂的,耗时较长的运算放在后台线程中完成,而让前台线程与后台线程通过消息交互.(注意:后台线程是不可以直接操作window对象和dom树的)
创建后台线程的核心就是HTML5 提供的 Workers API ,下面是我用这个API开发一个相对比较复杂的应用。
假设我们有一个需求,页面上有一个输入框,让用户输入一个整数,然后计算出所有小于这个整数的素数,然后把所有的素数分别计算平方,最后吧结果显示在主页面上。
如果不用HTML5,那么将会是非常长的一段JS代码,而且如果输入的值比较大时,它会吧整个页面全部“冻结”,有了HTML5就轻松许多了,我们分工如下:
主线程:把主页面上的输入框的中的数发送给后台线程1
后台线程1:根据这个数来找到所有比它小的素数,然后把这些素数封装在一个JSON数组中发送给主线程,同时把这一步骤的处理时间发送给主线程
主线程:收到后台线程发过来的信息,然后把素数列表发给后台线程2
后台线程2:对于素数列表中的每一个素数,计算求出其平方,然后把所有的结果以JSON数组形式发送给主线程,同时把这一步骤的处理时间发送给主线程
主线程:把后台线程1,后台线程2所得到的结果以及每个线程执行各自所用的时间显示在主页面上指定id的区域内
所以,我们首先要有个主页面,用来接受用户输入和最终显示结果,这个页面代码如下:
HTML>
charset="utf-8"
/>
HTML5 线程交互例子src="js/mainthread.js"
>
这个例子演示HTML5中的Worker的用法,它可以让主线程和多个后台线程合作来完成一项任务
步骤概述:
主线程:把主页面上的输入框的中的数发送给后台线程1
后台线程1:根据这个数来找到所有比它小的素数,然后把这些素数封装在一个字符串对象中发送给主线程,同时把这一步骤的处理时间发送给主线程
主线程收到后台线程发过来的信息,然后把素数列表发给后台线程2
后台线程2:对于素数列表中的每一个素数,计算求出其平方,然后把所有的结果发送给主线程,同时把这一步的处理时间发送给主线程
主线程:构造html页面,显示出每个步骤的处理时间,以及最终显示结果
输入一个正整数:
其次,我们来构建主线程,它主要任务就是进行协调和调度,它先取得用户输入,然后发送消息给后台工作线程1,之后接收从后台工作线程1返回的内含素数数组的消息,并把这个数组发给后台工作线程2,再接收从后台工作线程2中返回的内含素数平方数组的消息,最终把所有消息汇总然后打印在主页面上,细节请看我的代码注释:
mainthread.js
//这个函数是页面上点击button的事件处理函数
functionhandleInputInteger(){
//让window对象创建第一个后台线程(worker1),这个后台线程专门负责得到小于等于某个整数的所有素数
varworker1=newWorker("js/worker1-getPrimeList.js");
//获取用户输入的整数并转为10进制
varintegerInput=document.getElementById("inputedInteger").value;
varnum=parseInt(integerInput,10);
//把从用户输入的整数传给后台线程1
worker1.postMessage(num);
//当获取从后台线程1(worker1)传递过来的结果时,我们处理这个结果,我们这个结果是个json对象
worker1.onmessage=function(event){
//取得从后台线程1(worker1)返回的JSON对象,并且分离出信息
//其中primeArray是由后台线程1(worker1)完成的取得所有<=指定整数的素数构成的素数数组(还是未解析的JSON数组)
//而worker1ElapsedTime则是后台线程1(worker1)完成它的工作的用时,单位毫秒
vardata = event.data;
vardataInfo=JSON.parse(data);
varprimeArray=dataInfo.primeArray;
varworker1ElapsedTime=dataInfo.worker1ElapsedTime;
//我们让主线程创建第二个后台线程(worker2),这个后台线程可以把所有的素数计算平方
varworker2 =newWorker("js/worker2-getSquaredPrimeList.js");
//把primeArray的信息发送给第二个后台线程(worker2)
worker2.postMessage(primeArray);
//当获得从后台线程2(worker2)传来的结果时,我们在主页面上构造信息块,反映出整个过程
worker2.onmessage=function(event){
//取得从后台线程2(worker2)返回的JSON对象,并且分离出信息
//primeArray是从主线程传递给后台线程2(worker2)的素数数组
//squaredPrimeArray是由后台线程2(worker2)完成的所有的素数的平方构成的数组
//而worker2ElapsedTime则是后台线程2(worker2)完成它的工作的用时,单位毫秒
vardata = event.data;
vardataInfo=JSON.parse(data);
varprimeArray=dataInfo.primeArray;
varsquaredPrimeArray=dataInfo.squaredPrimeArray;
varworker2ElapsedTime=dataInfo.worker2ElapsedTime;
//取得素数的个数
varnumOfPrimes=JSON.parse(primeArray).length;
//构造信息文本,并且显示在页面上id为"result"的区域
varresult=document.getElementById("result");
varresultStr="用户输入的整数为: "+num+"
";
resultStr+="
";
resultStr+="后台线程1(worker1)经过分析可知,小于该整数的素数一共有"+numOfPrimes+"个。"+"
";
resultStr+="后台线程1(worker1)得到了素数数组为:"+primeArray+"
";
resultStr+="后台线程1(worker1)的操作用时为: "+worker1ElapsedTime+"毫秒!"+"
";
resultStr+="
";
resultStr+="后台线程2(worker2)得到的素数的平方数组为:"+squaredPrimeArray+"
";
resultStr+="后台线程2(worker2)的操作用时为: "+worker2ElapsedTime+"毫秒!"+"
";
result.innerHTML=resultStr;
}
};
}
再次,我们来定义后台工作线程1(worker1),这个线程必须被包含在一个js文件中,它必须定义当接收从主线程传来的消息(一个整数)时的处理函数,它会构造一个小于这个整数的所有素数组成的素数数组,并把这个素数数组返回给调用者线程(主线程)、当然了,我们还需要一个辅助方法来判断某个整数是否为素数,所以worker1-getPrimeList.js的代码如下:
//这个函数用于判断某个数是否为素数
functionisPrime(number) {
if(number
returnfalse;
} else{
for(varj = 2; j <= Math.sqrt(number); j++) {
if(number % j == 0) {
returnfalse;
}
}
}
returntrue;
}
// 这个js代码可以接收传入的整数并且返回所有小于等于这个整数的素数列表
onmessage = function(event) {
//计算开始时间
varbeginTime =newDate().getTime();
// 获取用户输入的整数
varnum = event.data;
// 构造一个buffer素数数组
varbufferedPrimeArray =newArray(num);
varprimeArrayMaxIndex=0;
for(vari = 0; i
if(isPrime(i)){
bufferedPrimeArray[primeArrayMaxIndex]=i;
primeArrayMaxIndex++;
}
}
//构造真正的素数数组
varprimeArray =newArray(primeArrayMaxIndex);
//将buffer素数数组中的所有的素数移动到这个数组中
for(varj=0;j
primeArray[j]=bufferedPrimeArray[j];
}
//计算结束时间
varendTime=newDate().getTime();
varelapsedTime=endTime-beginTime;
//构造json对象来存储信息:
vardata =newObject;
data.primeArray = JSON.stringify(primeArray);
data.worker1ElapsedTime= elapsedTime;
varstr=JSON.stringify(data);
//把这个json对象发送回主线程 (也就是创建这个worker1的线程)
postMessage(str);
}
最后,我们来定义后台工作线程2(worker2),这个线程也必须被包含在一个js文件中,它必须定义当接收从主线程传来的消息(一个素数数组)时的处理函数,它会构造一个对应的素数平方数组,并把这个素数数组,以及素数平方数组返回给调用者线程(主线程),所以worker2-getSquaredPrimeList.js的代码如下:
// 这个 js代码可以把传入的素数数组中每个素数依次求平方,然后构造一个素数平方数组
onmessage = function(event) {
//计算开始时间
varbeginTime =newDate().getTime();
// 获取从主线程传来的素数数组,并且还原成真正的素数数组
varprimeArrayFromMainThread= event.data;
varprimeArray = JSON.parse(primeArrayFromMainThread);
varlength=primeArray.length;
//构造一个素数平方数组
varsquaredPrimeArray=newArray(length);
//将原来数组中的所有素数依次求平方,然后填充到这个新的平方数组中
for(vari=0;i
squaredPrimeArray[i]=primeArray[i]* primeArray[i];
}
//计算结束时间
varendTime=newDate().getTime();
varelapsedTime=endTime-beginTime;
//构造json对象来存储信息:
vardata =newObject;
data.primeArray = JSON.stringify(primeArray);
data.squaredPrimeArray=JSON.stringify(squaredPrimeArray);
data.worker2ElapsedTime= elapsedTime;
varstr=JSON.stringify(data);
//把这个json对象发送回主线程 (也就是创建这个worker1的线程)
postMessage(str);
}
最后国际惯例,我们演示下例子:
首先,用户输入前:
用户输入某个整数,比如98,则显示如下: