Web Workers
撰写时间:2023-12-13
修订时间:2024-04-09
概述
初识Web Worker
主线程的代码:
上面的代码,主线程孵化了一个Worker线程,用以执行worker.js文件的内容。
线程之间通过postMessage传递数据,并响应onmessage事件以接收数据。
worker.js的代码:
这种只能通过调用它的脚本来访问的Worker类,称为dedicated worker.
Web Workers的应用
多个线程同时工作,可在一定程度上提高应用效率。
下面,我们准备使用Web worker来计算从1加到80亿的和。
taskDispatcher.js的内容:
根据要求的线程数量,为它们平均分配计算量。sum.js文件的内容:
运行程序,当只有一个线程时,所需时间为9669.00
毫秒,即9.7秒。将线程数量改为4
,所需时间为2342.00
毫秒。将线程数量改为10
,所需时间为1704.00
毫秒。如果再继续增大线程数量,则可能还可以再提升一点性能。例如,线程数为50时,时间为1666.00毫秒。但太多线程,可能适得其反,速度反倒变慢了。因此,需谨慎选取最合适的线程数量。
多少个Worker才最好
主线程:
任务分派:
可能的结果:
排在最上面的是最快的。调节要计数的范围,排序结果也不一样。因此,可根据具体任务量的多少,修改运行此程序来确定最合适的Web workers的数量。
OffscreenCanvas
OffscreenCanvas,也即离屏Cavnas,可无须事先创建任何一个Canvas就可在后台进行渲染。渲染完成后,可将渲染结果快速地绘制至任意一个前台的Canvas之上。
初识OffscreenCanvas
在下面的例子中,网页上有2个Canvas对象。
它们的大小分别为:
我们要使用OffscreenCanvas进行2次离屏渲染,然后,将这些渲染结果分别绘制到上述两个Canvas上面。
为使代码更加灵活,我们使用了回调机制。首先,在OffscreenCanvas的原型上添加一个renderInto方法:
该方法先使离屏Canvas的宽度与高度分别与参数targetCanvas保持一致,然后,将一个CanvasRenderingContext2D对象注入到参数callback回调函数中,这样,调用者可利用该CanvasRenderingContext2D对象进行渲染。
之后,调用OffscreenCanvas的transferToImageBitmap方法将渲染结果存储为一个ImageBitmap的实例变量imageBitmap。
最后,调用targetCanvas的getContext('bitmaprenderer')
方法,返回一个ImageBitmapRenderingContext实例,该实例只有一个方法transferFromImageBitmap,用以接收所渲染完毕的结果。
可以多次调用transferToImageBitmap,并向多个canvas传递。
客户端代码如下:
运行应用。
从代码运行效果来看,即先离屏渲染两个图像,然后再分别显示到两个canvas上面。
transferControlToOffscreen
OffscreenCanvas还可以通过Canvas的transferControlToOffscreen方法来获取。
渲染完成后,offscreenCanvas自动将渲染结果立即绘制到canvas上。
运行应用。
这种方式,与普通的Canvas渲染看似相似。但我们还可以更进一步,将渲染环节交由Web worker来完成。
Offscreen Canvas Web worker
先编写下面代码:
当我们运行上面的代码,就会出现错误:
这是因为Worker的postMessage方法,只接受能处理structured clone算法的JavaScript对象。Transferable objects支持这种算法。Canvas不属于Transferable objects,而OffscreenCanvas属于Transferable objects。因此,我们需要将代码改为:
postMessage方法的第2个可选参数是一个数组,里面列出所有需要转移支配权(ownership)的Transferable objects。只有显式地列出这些可转移支配权的对象,才能在不同的线程中安全地交换数据。
在不同线程中转移支配权,意味着在任一时刻,只有在拥有支配权的线程中,我们才能安全地访问该对象。看下面的代码:
在offscreenCanvas转移支配权之前,我们可以访问其width属性值。
而在将offscreenCanvas的主线程的支配权转移至Worker线程后,我们在主线程中若访问其width属性值,将得到错误的数值。除非分支线程重新转移回其支配权。
而由于canvas并非转移其支配权,因此在主线程中我们仍可安全地访问其各个属性值。
下面是canavs-worker.js的内容:
由于我们不需要在主线程中对offscreenCanvas进行进一步的操作,因此分支线程无需返回offscreenCanvas及其支配权。
运行应用。
在分支线程中实现动画
主线程代码:
分支线程代码:
运行应用。