第6章:快速响应的用户界面

浏览器UI线程P107

浏览器限制

浏览器限制了JS任务的运行时间,此类限制分两种:调用栈大小限制和长时间运行脚本限制。长时间运行脚本被称为长时间运行脚本定时器或失控脚本定时器。第一种方法是记录自脚本开始以来执行的语句的数量;第二种是记录脚本执行的总时长。

如果界面在100ms内响应用户输入,用户会认为自己在直接操作界面中的对象。超过100ms意味着用户会感觉到自己与界面失去联系。

使用定时器让出时间片段

定时器基础

在JS中使用setTimeout()setInterval()创建定时器,它们接收相同的参数:要执行的函数和执行前的等待时间(ms)。setTimeout()函数创建了一个只执行一次的定时器,而setInterval()创建了一个周期性重复运行的定时器。

定时器代码只有在创建它的函数执行完成之后,才有可能被执行。定时器代码会重置所有相关的浏览器限制,包括长时间运行脚本定时器。此外,调用栈也在定时器的代码中重置为0这一特性使得定时器成为长时间运行JS代码理想的跨浏览器解决方案。

定时器精度

JS定时器延迟通常不太精准,相差大约几毫秒。定时器延时的最小值有助于避免在其他浏览器和其他操作系统中的定时器出现分辨率问题。

使用定时器处理数组

使用定时器取代循环的两个决定性因素:

基本的异步代码模式:

items = ["a", "b"];
var todo = items.concat();//克隆原数组
setTimeout(function () {
    //取得数组的下个元素并进行处理
    process(todo.shift());

    //如果还有需要处理的元素,创建另一个定时器
    if (todo.length > 0) {
        setTimeout(arguments.callee, 25);
    } else {
        callback(items)
    }
});

分割任务P116

记录代码运行时间

通过原生的Date对象来跟踪代码的运行时间。每个新创建的Date对象以当前系统时间初始化,可以定时创建Date对象并比较它们的值来记录代码运行时间。

定时器与性能

在Web应用中限制高频率重复定时器的数量,创建一个独立的重复定时器,每次执行多个操作。

Web Workers

Web Workers API引入了一个接口,能使代码运行且不占用浏览器UI线程的时间。

Worder运行环境

Web Workers没有绑定UI线程,它们不能访问浏览器的许多资源。Web Workers从外部线程修改DOM会导致用户界面出现错误,每个Web Workers都有自己的全局运行环境,其功能只是JS特性的一个子集。

Web Workers运行环境:

Web Workers有着不同的全局运行环境,因此无法从JS代码中创建,需要创建一个完全独立的JS文件,其中包含了需要在Worker中运行的代码。

要创建人工线程,必须传入JS文件的URL:

var worker = new Worker('code.js');

一旦代码执行,将为这个文件创建一个新的线程和一个新的运行环境。该文件会被异步下载,直到文件下载并执行完成后才会启动此Worker。

与Worker通信

Worker与网页代码通过事件接口进行通信。网页代码可以通过postMessage()方法给Worker传递数据,它接收一个参数,即需要传递Worker的数据。此外,Worker还有一个用来接收信息的onmessage事件处理器。

var worker = new Worker('code.js');
worker.onmessage = function (event) {
    console.log(event.data);
};
worker.postMessage("Andy");

加载外部文件

Worker通过importScript()方法加载外部JS文件,该方法接收一个或多个JS文件URL作为参数。importScript()的调用过程是阻塞式的,直到所有文件加载并执行完成后,脚本才会继续运行。由于Worker在UI线程之外运行,所以这种阻塞不会影响UI响应。

实际应用

Web Workers适用于处理纯数据,或者与浏览器UI无关的长时间运行脚本。

解析一个大字符串只是许多受益于Web Workers的任务之一。其他受益的任务:

小结

高效管理UI线程就是要确保JS不能运行太长时间。