1. 背景
JavaScript刚问世的时候,没有人担心它的性能,它只是一种简单的语言,可以在网页运行小段的脚本。而现在,JavaScript已经成为web开发领域的王者,只要想给网页添加交互性,开发人员就会用到它。然而,JavaScript与它现在的地位相比,仍然有一些不相称的地方。
比如,JavaScript处理大计算量任务时会始终在前台运行,因而耗费时间的代码会打断用户,阻塞页面,直到任务完成,导致用户体验很不好。
2. Web Worker的诞生
HTML5提出了一个很好的解决方案,一个叫Web Worker的对象,能够在后台完成工作;在它工作期间,可以通过文本消息这种安全的方式与它通信。
Web Worker适合用于那些费事的任务,换句话说,不应该用Web Worker来执行简单的任务。比如下图的搜索素数的任务,计算量很大。
很明显,可以使用Web Worker来编写这个页面
下面先来看一看这个例子的HTML代码
|
|
页面使用了两个input控件,用于区间的输入,一个用于搜索的按钮和取消的按钮,还有两个div分别用于显示结果和状态信息。
页面的js代码有点长,大概的操作是先取得输入框的两个数值,开始搜索,然后把找到的素数添加到页面中。其中搜索的任务是由另一个函数来完成的,保存在另一个js文件中(要理解Web Worker的作用,其实不必去看该函数,只要知道是个费时间的任务就可以了)。下面是dosearch函数的代码:
|
|
可以看到,代码简明扼要,但是如果搜索的区间很大,搜索速度将会很慢,导致页面数分钟内没有反应,不能单击,滚动等。
3. 把任务放在后台
Web Worker为解决这个问题定义了Worker对象,下面这行代码创建了一个新的Worker对象
|
|
让Web Worker运行的代码要放在一个单独的js文件中,同时Web Worker是把相应的搜索结果通过页面的js代码渲染到页面中,不能通过PrimeWorker.js中的代码直接操作页面的HTML元素。
- 页面与Web Worker之间通过消息来沟通,给Worker发送消息是使用该对象的postMessage()方法,然后Worker对象会通过onMessage()方法接收到该数据的一个副本,开始工作。
- 同样地,Worker如果跟网页对话,也是调用自己的postMessage()方法,带上参数,网页也同样是在onMessage()方法中接收这些数据。
需要注意的是:调用postMessage()方法的时候,只能给它传入一个值,所以通过把这两个值放到一个对象中,如下:
|
|
好了,了解了Web Worker的一些基本定义后,我们来对前面的dosearch函数进行修改,不再让它自己搜索素数,而是创建一个Worker来承担相应的任务:
|
|
下面,该PrimeWorker.js出场了,它接收到onMessage事件,执行搜索,然后发送给网页结果数据:
|
|
在Worker调用postMessage()将结果返回的时候,会触发网页onMessage事件,进而调用receivedWorkerMessage()函数:
|
|
总的来说,代码的结构变了,但是逻辑相差无几。可是现在即便是搜索区间变得很大,页面也能保持相应,可以进行操作,这都是Web Worker的功劳。
4. Web Worker错误处理
Web Worker用onerror事件告诉页面有错误发生:
|
|
这样,如果后台脚本有问题Worker就可以把错误发送给网页:
|
|
有一个需要注意的就是,用来测试的js和html文件都需要发布到站点才可以正常访问,否则会提示这个错误:
查找了一些资料原因大概就是谷歌浏览器安全机制导致的,不允许在本地直接运行,可以用其他浏览器测试或者部署到本地的服务器上测试。
5. 取消后台任务
停止Worker工作的方式有两种,一种是Worker对象调用自己的close()方法,更为常用的是创建Worker对象的页面调用该对象的terminate()方法。如下就是取消按钮的事件函数:
|
|
这样停止Worker之后,就不能给它发送消息了,也不能让它执行任何操作了。要执行新的搜索任务,就要重新创建新的Worker对象。
6. 显示进度信息
要显示进度,Web Worker必须在工作的同时把进度百分比发给页面。
我们可以重写PrimeWorker.js的代码:
|
|
页面在接受信息之后,想要检查信息的类型,如果过接收到的是搜索结果,则将结果显示在网页中;如果是进度信息,则更新状态消息:
|
|
总结
Web Worker把任务放在后台,适合用于那些费时的任务,通过postMessage()和onMessage()方法传递数据,大大的提高效率。