收藏
评论

使用worker开启新线程进行耗时运算官方


你好,我是李艺。

上节课我们主要学习了如何使用并发复合命令,这节课我们学习使用worker。

首先看一个问题,就是worker它并不属于JS语言,标准的JS语法非常简单,仅包括基本的数据类型以及日期,数学 正则表达式等等这些对象,worker它是HTML5首先被扩展出来的,依托于寄主的环境而存在的,它是一种可以在后台并行执行JS代码,不影响页面渲染的这样一个技术,我们可以将worker看作一种允许开发者手动开启的一个异步线程,使用它,我们可以在异步线程中执行一些比较耗时的计算代码,在计算代码执行完成以后再将执行结果同步给主线程使用,下面我们看项目实践。


首先看实践一,创建worker线程。

怎么才可以创建worker线程,首先我们需要创建一个专用目录用于存放我们的worker代码,然后在app.json配置文件里面添加一个workers的配置节点,这个目录名一般是复数的形式,代表在这个目录下面可以有很多的worker文件,并不是只能放一个worker的代码,它不像WXS脚本,WXSjia只支持ES5的语法 worker代码支持ES6语法,在我们这个项目里边有一个workers目录,这个目录下面只有一个index.js文件,它的主要功能就是接收一个毫秒参数,然后计算并格式化这个时间字符串,这个文件是为我们接下来要创建的,这个stopwatch_wk这个组件进行服务的,

下面我们进行实践一的代码演示。

首先我们需要在我们这个项目的目录下面创建一个专门用于存放worker代码的目录,一般这个目录我们用复数的形式workers,然后在我们的app.json文件里边再放一个关于workers的配置,配置的位置其实无所谓,放在上面下面都是可以的,workers这个目录的名字也是workers,这样就可以了,再下一步我们要完成我们的里边目录下面的一个index.js的一个代码,看一下我们最终的一个代码,找到这个workers目录下面有一个index.js,这个就是我们代码 将这个代码拷贝一下看一下我们这个代码主要做了什么事情,首先这个地方有一个worker,这个worker它不需要我们声明,它在我们的当前的worker线程里面,我们可以看作它是一个实例变量,是可以直接进行使用的,它就是代表我们当前worker环境里面默认的一个实例名,在这个上面我们可以调用两个方法,也是常用的方法一个是onMessage,onMessage就是监听来自主线程的消息,监听以后这里面是一个回调函数,在这个回调函数其中有一个对象,我们从这个对象里面析构出来一个ts的参数,这里边是对ts的一个运算,运算完了以后我们要拼接出result这个结果,这个代码跟我们之前在stopwatch里面、写的那些JS代码都是一样的,计算完成以后 在最后我们调用到这个worker的postMessage,我们将结果传给了我们主线程,这就是它的主要的代码 一个接收,然后一个发送就可以了,这个代码演示我们就说到这里。


下面我们看实践二,创建stopwatch_wk组件。

这个stopwatch_wk组件,它和我们原来的这个stopwatch_go组件一样,在这个组件里面它只负责调用worker计算到的结果,然后在这个视图上进行渲染,这个文件的wxml代码以及它的wxss代码和我们原来的组件里边的设置都是一样的,不需要修改。

下面我们看实践二的代码演示。

目前我们worker以及它里边的文件代码已经创建完成了,下面我们创建使用worker代码的一个组件,这个组件与其他的组件一样,仍然是放在index_addons目录的下面,这个地方这就是我们的wk,就是我们stopwatch_wk这个代码,wk就代表的是worker,我们准备调用那个worker代码,然后这个代码已经在这里,我们看一下它的主要的一个实现。

json配置文件没有修改 都是一样的,然后wxml代码这个也没有修改,样式代码与之前也是一样的,主要差别在index.js JS代码,首先最上面是属性 组件的属性,再往下是data 这个与之前都是一样的,然后下面stop方法,我们这个地方多了一个关于worker的判断,在这个组件我们调了停止以后,我们调用它的terminate,它的销毁的方法,我们主动地去销毁我们worker的线程,并且把它置为null,这是一个释放资源的做法,然后switch方法没有修改,跟原来还是一样的,主要的差别还是在start这个方法里面。

在这个地方我们看一下,首先在这里this.worker等于wx.createWorker,这个地方我们直接写了一个页面,直接写了一个文件地址workers/index.js,这个地址就是我们前面所创建的 在这个目录下面 index.js它的地址,而且这个地址我们不用写相对地址,直接写相对我们项目根目录的这样的一个地址就可以了,还有一点我们需要说明 这个文件它不是随便的,只有我们在配置文件里面配置了我们workers目录,它是worker目录以后才可以去引入里边的文件,然后创建这样的一个worker线程,否则创建是不成功的,创建完成以后 接下来我们就用onMessage,onMessage前面,我们在worker的文件里面已经说过了它相当于是这个消息的监听,这个监听 只不过跟那个不一样的是 这个监听它是监听来自worker线程发过来的信息,给主线程发的消息,然后监听拿到这个结果以后,把这个结果给它调用setData,然后往这个视图上进行更新,这个地方还是原来的convertTimeStampToString,是原来的方法,只不过这个里面它在执行的时候我们是调用worker它的postMessage,调用它的这个方法,然后将这个数据 ts数据然后传递进去,计算是由worker线程然后进行计算的,计算完了以后它再发过来 通过onMessage然后接收,进行更新,是这样的一个流程。

下面这个地方是一个setInterval跟我们原来是一样的,就是我们要设一个定时器,同样是为了防止这个程序卡顿处理不过来,也加了一个flag的变量,flag的变量每次在这个地方,在每次我们收到worker线程发过来消息以后把它置为true,置为true以后在这个地方定时器它就可以调用了,是这样的一种方式,这个定时器我们可以将它改小一点改成100,跟我们原来的频率改成一样的一个数字,这个就是我们的stopwatch_wk它的一个代码实现了,下面我们开始测试一下它的表现,在我们首页里面打开json配置文件,然后这个地方是我们这个组件的一个引入,我们将这个go改成wk,这就是用我们现在创建的组件了,代码准备完了,然后单击编译进行测试,单击这个组件,现在它开始工作了。

我们可以打开Memory面板,在Memory面板里面我们可以看到下面它是有这个线程的,我们先让它停止,这个组件可能已经不堪重负导致我现在开发者工具整个软件都比较卡顿了,所以现在单击面板以后它没有反应了,现在终于有了反应,但是反应很迟钝,重新编译一下 然后让页面重启一下,也说明我们刚才有一个地方的改动就是组件里面定时器设成100这个频率还是有点高,我们可以将这个频率给它再降一下 降下来找到我们的组件的源码,然后在这里仍然给它改成300然后刷新重启一下项目,已经预感到这个组件的执行效率已经不是很高了,它运行以后直接让我们的工具非常的卡顿,几乎要卡死了,看一下我们这个代码。

趁这个软件在启动的时候,我们看一下这个代码,看我们这个代码还有什么可以优化的一些地方,首先在这个地方有一个关于this对象的一个别名的设置,我们把它设成为了self,然后在这个里面我们看一下哪个地方用到了self,这个地方有,这个地方有,然后这个我们可以改成箭头函数,可以改成箭头函数,这个可以给它注掉把这个换成this,改完了,现在我们再继续看我们的项目看看有没有启动起来,没反应,强制退出,这个项目终于启动了,我们重新开始一下测试,首先我们可以看一下我们的Memory面板,这个Memory面板 这个地方我们可以单击一下,看到没有,当我们单击以后,下面它其实多了一个worker线程对吧,worker.js对不对,这个其实就是我们组件 它创建的worker线程,当我们再次单击的时候 我们调了它的stop,因为stop里面有一个调用了worker对象的terminate方法,就把它worker对象给它销毁了,所以我们停止以后可以看到它worker线程又消失了。

另外我们再看一下Performance面板 性能面板,现在我们打开录制,让组件开始执行,单击一下开始执行,让它停止 执行一点点时间就可以了,现在我们可以看一下它的糟糕的一个表现,整体来看有很多倒三角,红色的倒三角,然后这个地方这有一个setData,这个地方我们可以看一下它是什么,好像还不是setData触发的,这个地方是对不对,然后前面这个,这个里面也有对吧,我们先看稍微短一点的时间耗时是多少,78.24ms对吧,这个是158.66ms已经相当长了,这个时间是86.97ms,这个消耗已经非常长了,而且我们现在的帧频是300毫秒,300毫秒的一个触发帧频定时器,比我们原来的100毫秒其实还要更宽泛一点,还要更宽,帧频没有那么高,这种情况下它性能表现其实已经是很差,代码演示我们就看到这里。


最后我们总结一下,使用worker可以做很多事情,可以将费时的数据运算放在worker线程里面,以免影响页面的渲染性能,但是在刚才我们示例演示里边我们也看到了,虽然我们将计算代码放到了worker线程里面,但是由于我们组件里面频繁地调用了setData 这种形式之下,其实也影响了我们这个页面渲染,它是一个整体,不能片面地去看这个事情,另外就是放在wxs这个代码里面和放在worker中的代码,具有同样的一个效果,在渲染上具有同样的结果,因为它都是异步执行的,但是worker它相比wxs限制也有很多,我们可以列举几点一起来看一下,例如第一点不能使用wx开头的小程序接口,第二点在workers目录下面可以有很多的worker文件,但同时只能有一个worker线程在开启,也就是刚才我们在Memory的面板里,我们可以看到手动开启的worker线程,同时只能存在一个,如果你想要开新的话,需要将原来的先给它停掉,先销毁掉,并且如果这个系统资源如果紧张的话,worker线程还有可能被系统回收掉,它是有这样的一个危险的,第三点就是worker只能放在特定的 已经配置好的目录下面,不能随意地放置 放在其他目录下是不可以的,最后一点就是通讯也很不方便,worker线程和主线程之间的通讯只能使用postMessage和onMessage进行相互的这样一个通讯,这是相当于观察者模式的一种通讯机制,综上所述worker可以在一定程度上,在特定的场景之下减轻这个页面的计算压力和渲染压力,但是它并不是提高页面渲染性能的一个最佳选择,worker适合在后台做一些有规律的、常规的一些大数据的运算工作,这节课我们就讲到这里,,上面我们看到的这些网址是本节课涉及到的文档地址。

点击查看开放文档:


这节课我们学习了如何在小程序里面开启worker线程,下节课学习如何在后端异步执行运算代码。

最后说一下思考题。这里有个问题请你思考一下,现在有一种游戏叫云游戏,游戏的运算和画面渲染完全是放在云端服务器上完成的,如果这个网速足够快和稳定的话,这种游戏其实丝毫感受不到它的卡顿,在小游戏(小程序)里边是不是也可以将一些逻辑计算代码放在后端,由后端代码进行计算,然后后端代码计算完了再传给前端,我们是不是可以这样去使用,这个问题先留给你思考一下,下节课我们深入探讨一下这个问题。

最后一次编辑于  2022-07-14
赞 7
收藏
登录 后发表内容

小程序性能优化实践

课程标签