《高性能javascript》读书笔记:第六章 快速响应的用户界面

浏览器限制了javascript任务的运行时间,调用栈大小限制和长时间运行脚本限制。

a,IE自第4版开始,设置默认限制为500万条语句。此限制放在windows注册表中,
叫HKEY_CURRENT_USER\Software\Microsoft\InternetExplorer\Styles\MaxScriptStatements

b,FireFox的默认限制为10秒,该限制放在配置设置中(通过地址栏输入about:config),键名dom.max_script_run_time

c,Safari的默认限制为5秒,该限制无法更改,但是可以通过Develop菜单选项Disable Runaway JavaScript Timer来禁用定时器

d,Chrome没有单独的长运行脚本限制,替代做法是依赖其通用崩溃检测系统来处理此类问题

e,Opera没有长运行脚本限制,它会继续执行javascript代码直到结束,鉴于Opera的架构,脚本运行结束时不会导致系统不稳定

  

单个js操作花费的总时间不应该超过100毫秒

IE会控制用户交互行为触发的js任务,因此它会识别连续两次的重复动作。例如,当有脚本运行时点击一个按钮四次,最终按钮的onclick事件处理器只被调用两次。

 

定时器

有一些复杂的js任务不能在100毫秒或更短时间内完成,这个时候就要让出UI线程的控制权停止执行js,使得UI可以更新,然后再继续执行js。此时需要用定时器。

 

创建定时器的两种方式

setTimeout()  //创建一个只执行一次的定时器

setInterval()   //创建一个周期性重复执行的定时器

这两种方法基本相同,最主要的区别是如果UI队列中已经存在由同一个setInterval()创建的任务,那么后续任务不会被添加到UI队列中。

  

示例

function greeting(){
  alert("Hi");
}

setTimeout(greeting,250);//这段代码将在250毫秒后向UI队列插入一个任务,不一定是250毫秒后执行



定时器会重置所有相关的浏览器限制,并且调用栈也在定时器的代码中重置为0,所以这一特性使得定时器成为长时间运行JS代码理想的跨浏览器解决方案。

定时器通常不太精确,相差大约几毫秒,所以不可用于测量实际时间。

windows系统中定时器分辨率为15毫秒,所以设置定时器延时小于15毫秒将会导致IE锁定,所以延时最小值建议为25毫秒(实际时间有偏差而成15毫秒或30毫秒)以确保至少有15毫秒延时。

 

循环数组时如果数组太长或处理太费时,并且处理过程不需要同步,也不需要按顺序处理,则应该把循环的工作分解到一系列定时器中。

for(var i=0,len=items.length;i<len;i++){

  process(item[i]);

}

  

应该改成

var todo=items.concat();//克隆原数组

setTimeout(function(){

  process(todo.shift());//取得数组的下个元素并进行处理,并把取得的这个元素从todo数组中删除

  if(todo.length>0){ //检查是否还有更多的元素需要处理,如果有,那么就再启动一个定时器,否则调用callback()函数

    setTimeout(arguments.callee,25); //因为下一个定时器需要运行相同的代码,所以第一个参数为arguments.callee.该值指向当前正在运行的匿名函数

  }else{

    callback(items);

  }

},25);

该方法抽出为一可重用的函数

function processArray(items,process,callback){

  var todo=items.concat();

  setTimeout(function(){

    process(todo.shift());

    if(todo.length>0){

      setTimeout(arguments.callee,25);

    }else{

      callback(items);

    }

  },25);

}



上面的通用方法使用示例

var items=[22,542,542,63,224];

function outputValue(value){

  console.log(value);

}

processArray(items,outputValue,function(){

  console.log("完成");

});


同上,可把参数相同的一系列函数调用分割到不同的定时器中执行,通用方法如下:

function multistep(steps,args,callback){

  var tasks=steps.concat();

  setTimeout(function(){

    var task=tasks.shift();

    task.apply(null,args||[]);

    if(tasks.length>0){

      setTimeout(arguments.callee,25);

    }else{

      callback();

    }

  },25);

}



使用示例

function saveDocument(id){

  var tasks=[openDocument,writeText,closeDocument,updateUI];

  multistep(tasks,[id],function(){  //此处的[id]为数组

    alert("保存完成");

  });

}

  JS中可以通过Date对象来跟踪代码的运行时间,如下

var start= +new Date();//用+号可以将Date对象转化为数字,那么在后续的数学运算中就无须转换了

stop;

//在这里运行的能测出运行的时间

stop= +new Date();

alert(stop-start);//弹出提示显示运行了多少毫秒



Web Workers

js没有办法在浏览器UI线程之外运行代码.Web Workers API引入了一个接口,能使代码运行且不占用浏览器UI线程的时间.每个新的Worker都在自己的线程中运行代码,不会影响浏览器UI,也不会影响其它Worker中运行的代码.

WebWorkers被Firefox3.5,Chrome3,Safari4原生支持.

Worker运行环境由如下部分组成:

一个navigator对象,只包括四个属性:appName,appVersion,userAgent,platform

一个location对象(与window.location相同,不过所有属性都是只读的)

一个self对象,指向全局worker对象

一个importScripts()方法,用来加载Worker所用到的外部js文件

所有的ECMAScript对象,诸如Object,Array,Date等

XMLHttpRequest构造器

setTimeout()和setInterval()方法

一个close()方法,它能立刻停止Worker运行

  

 

由于Web Worker有着不同的全局运行环境,因此无法从js代码中创建它.事实上,需要创建一个完全独立的js文件,其中包含需要在Worker中运行的代码.要创建Worker线程,必须传入这个js文件的URL:

var worker=new Worker("code.js");//此代码一旦执行,将为这个文件创建一个新的线程和一个新的Worker运行环境.该文件会被异步下载,直到文件下载并执行完成后才会启动此Worker



网页代码可以通过postMessage()方法给Worker传递数据,只有一个参数.Worker有一个用来接收信息的onmessage事件处理器.

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

worker.onmessage=function(event){

  alert(event.data);

};

worker.postMessage("Nicholas");//只能传递字符串,数字,布尔值,null,undefined,Object的实例和Array的实例。Safari4和Chrome3只允许传递字符串



Worker可通过它自己的postMessage()方法把信息回传给页面

//code.js内部代码

self.onmessage=function(){

  self.postMessage("hi"+event.data+"!");

};

  

消息系统是网页和Worker通信的唯一途径.

 

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

 

网页中应用的示例.示例流程即页面通过postMessage()向Wroker传递json字符串,Worker在它自己的onmessage事件处理器中接收此字符串然后解析,完成以后再通过postMessage传回页面.

var worker=new Worker("jsonparser.js");

//数据到位时,调用事件处理器

worker.onmessage=function(event){

  var jsonData=event.data;//JSON结构被回传回来

  evaluateData(jsonData);//使用JSON结构

};

worker.postMessage(jsonText);//传入大段要解析的json字符串



//Worker中负责解析json的代码如下:(jsonparse.js内部代码)

//当json数据存在时,该事件处理器会被调用

self.onmessage=function(event){

  var jsonText=event.data;//json字符串由event.data传入

  var jsonData=JSON.parse(jsonText);//解析,本示例假设此处特别费时(500毫秒)

  selft.postMessage(jsonData);//回传结果

};



优先使用Worker的情况(浏览器支持)

1,编码/解码大字符串

2,复杂数学运算,包括图像或视频处理

3,大数组排序

4,任何超过100毫秒的处理过程都应当考虑Worker方案是不是比基于定时器的方案更合适.

posted on 2012-02-22 21:47  LCM  阅读(595)  评论(0编辑  收藏  举报

导航