六、快速响应的用户界面
1、ui线程
浏览器用于执行javascript和更新用户界面的线程叫做ui线程,工作原理基于简单的队列系统。任务(包括js代码和界面更新--重绘回流)按照在队列的顺序依次执行。一个界面交互可能会引起多个任务。当前任务执行完毕,ui线程会执行下一个任务,知道队列中没有任务,这时浏览器ui线程处于空闲状态。
注意的一点是,任务运行期间如果与页面发生交互,对应的ui更新无法即时看到,可能新的ui任务也不会被创建并加入队列。
2、浏览器对ui线程的限制
大部分浏览器限制了javascript任务的运行,这是为了避免某些恶意代码通过永不停止的操作锁定浏览器(即可能出现界面长时间无响应),。这种限制一般是调用栈大小限制和长时间运行脚本限制。长时间运行脚本限制基本原理是建立一个定时器记录一个脚本的运行时间,又分为两种,一种是记录脚本执行的语句的数量,这意味着在不同的浏览器和计算机可能有不同的运行时间。另一种是记录脚本运行的总时长。不同浏览器对于这两种限制的规定也不同。(在成书时,ie4限制500万条语句,火狐默认限制为10s,safari限制为5s,chrome通过崩溃监测机制处理此类问题,opera则没有此类限制。)
3、多久才算合适
通常来说,单个javascript操作花费的时间不应该超过100ms。超过这个时间,用户会感觉到略微的延迟;小于这个时间,用户察觉不到。
在javascript运行时,用户的交互(例如点击按钮,按钮更改样式)引起的Ui更新不会执行,所引起的javascript任务会被加入到队列中。
4、主动让出ui线程
有时,一些复杂的javscript代码无法在100ms或者更短的时间内完成,这时可以考虑将代码拆分成若干个片段,写成异步形式。一个简单的例子如下:
将原有代码拆分成两部分,第一部分立即执行,第二部分在250ms之后加入到队列(如果此时Ui线程空闲,则立即执行;如果ui线程还在执行其他任务,会在此时加入这个新任务)
5、定时器精度
javascript定时器延迟通常不是十分准确,相差大约几毫秒,因此,尽量不要用定时器测量实际时间,尤其是几毫秒的时间跨度。
在window系统中定时器分辨率为15ms(经测试现在好像不存在这个问题了),所以定时器延迟最小值建议25ms。
6、使用定时器处理循环
对于同一个耗时过长的循环或者多个任务,,如果处理过程不需要同步且不需要按照顺序处理,那么可以用一个定时器记录循环执行的时间,然后将循环过程拆分。
注意记录定时器的id,同一时间应只有一个定时器存在,多个定时器会严重影响性能(间隔在1s或者以上的定时器几乎不会影响,这里指高频定时器)。
7、web workers
web workers引入了一个接口可以执行javascript代码,但不执行ui更新。它独立在ui线程之外(因本人目前对web workers了解不多,暂时认定它是ui线程的一个子线程,web workers这部分也是简单介绍).
workers与网页通过事件接口进行通信。网页可以通过PostMessage()方法给workeers传递数据。workers有一个onmessage的事件用于接受workers传递过来的信息。
在work.js(自己写的执行代码的js文件)内可以用postMessage()方法将数据传递给网页。(只支持简单类型以及array、object的实例。)
在index.js内部通过importScripts()方法加载外部js文件,可以接受多个js文件的路径作为参数。注意,importScripts()方法是阻塞式的,所有js文件加载完之后才会执行后续index的代码(相当于阻塞了worker,但没有阻塞网页,也就是ui线程)
web workers适用于处理纯数据或者与浏览器ui无关的、需要长时间运行的脚本。
web workers常用场景如下:
编码/解码大字符串、复杂数学运算(包括图像和视频处理)、大数组票排序以及其他的、合适的长时间任务。
更多web workers相关知识读者可自行查阅相关资料。