JavaScript性能提升之——提升响应速度

2017-05-21  本文已影响0人  SLEBEE

首先呢,这里说的响应速度,是指页面UI的响应速度,对一个用户界面来说,评判快的标准是什么?

用户觉得快才是快

优化UI线程

说到底产品最终都是给用户使用的,不论你对自己的产品做了什么优化,在用户手里好用就是好用,快就是快,不是靠你开发者说这说那用了什么技术啊来判定的。那么在网页中,怎么才会显得快呢?

提高用户界面的响应速度一般有两种方式:

说到提升响应速度,我们得先来说一下浏览器的UI线程。

浏览器UI线程

这个东西呢就是用来执行JavaScript和更新用户界面的进程啦。

UI线程是基于一个简单的队列系统的,所有的任务(UI更新、执行JavaScript)都会被放到任务队列中,任务会被保存到队列中直到进程空闲,一旦空闲,队列中的下一个任务就会被重新提取出来运行。也就是说对浏览器来说,页面上出现的所有事情,执行js、重绘页面这些都要一件件来做。

引用一下 《You Don't Know JavaScript:Types & Grammar,Async &Performance》书中的一段伪代码来解释下这个概念:

//eventLoop是一个用作队列的数组
var eventLoop = [];
var event;

//'永远'执行
while(true){
  if(eventLoop.length>0){
    event = eventLoop.shift();
    try{
      event();
    }
    catch(err){
      reportError(err);
    }
  }
}

结合这段概念代码,实际上我们的页面产生的所有操作,读取、解析资源、渲染页面、重绘,执行js等等这些任务全部都会在队列里排队执行。
当UI线程处于执行任务期间,如果用户在这个时候与之交互,会出现UI没有即时更新,更有可能出现UI的更新任务不会被创建,在我们平时看来就是点击了没反应,连按钮都没一点反馈变化,这种时候就是UI线程处于繁忙状态。

出现这种情况的时候很可能是因为你的js运行时间过长了,现在的浏览器有些会有限制运行时间,超过时间会弹出提示框。这种情况我觉得还是遇到的次数挺多的= =;

相当多的历史研究中有提到过,单个js的操作花费时间不应该超过100ms。

我们自己使用网页的时候也会有感觉,我点了这个按钮过了一会才有反应,完全就给了人一种这网站很慢的感觉。

使用定时器让出UI线程

虽然说单个js任务应该在100毫秒内完成,但是有时候有些任务确实要花费较长时间,这个时候可以使用定时器来让出UI线程使用权,我们在看别人代码的时候应该也有见过设置一个setTimeout任务但是延迟又设置的很低的用法,这就是使用定时器来让出UI线程。

首先要明确的一点是,定时器并不会一执行把你要延迟的任务加入到事件循环队列中,它只是设置定时器到时后,再把你要延迟的这个任务加入到队列中,这也是定时器精度可能不高的原因,因为如果你到时间的时候刚好前面还有很多任务没有被执行完,那你这个只能等啦。

用定时器取代循环

说那么多,什么情况下可以用定时器来做代码块拆分运行优化呢,很常见的js运行时间过长的例子是循环,有些循环处理函数过于复杂或者循环源的大小过大,我们就可以使用,但是这也是有前提的,使用定时器取代循环要满足两个条件:处理过程无需同步,数据无需按顺序处理。
满足这两个条件之后,我们可以写一个简单的函数来处理:

function processArray(items,process,callback){
  var todos = item.concat();
  setTimeout(function(){
    process(tudos.shift());
    if(tudos.length>0){
      setTimeout(arguments.callee,25);
    }else{
      callback(items);
    }
  })
}

只要调用这个函数并传入数组,处理方法与完成的循环完成的回调函数,这样做会使原本的循环时间变成,但是每次循环后都会让出UI线程(上面定时器让出UI线程有说明),不至于说让整个长时间的循环一直占着UI线程而锁定浏览器,虽然实际上的处理时间变长了,但是对用户来说却会觉得更快了。

这种定时器的用法还可以用于分割任务,一个运行时间过长的函数可以试着能不能拆分成多个函数,然后用类似上面的方式来加快UI响应速度。

要注意的是,同时创建多个重复的定时器会产生所有定时器一起抢占UI线程而产生性能问题,最好使用一个定时器每次执行多次操作而不要同时创建多个

使用Web Worker

这个功能属于提供js能以类似多线程的方式来运行的功能,不过实际上并不是。

首先要知道的是,HTML5的Web Worker这个特性属于宿主功能,即浏览器提供的功能,本身和JavaScript语言没关系,JS本身并没有任何支持多线程执行的功能。

浏览器环境可以提供多个JavaScript引擎实例并各自运行独立的线程上。利用web worker,我们就可以将程序划分多块来并发运行。

Web Worker通常被用于以下几个方面:

实例化一个Worker很简单: var worker1 = new Worker('http://aa.com/b.js')// 这个url要指向js文件位置;

当这个文件被加载到一个Worker之后,浏览器就会启动一个独立的线程来运行这个Worker。

web worker中使用大多数的标准javascript特性,包括

完整的Web Worker用法可以参考https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers

注意,Worker之间以及他们和主程序之间不会共享任何作用域或者资源,他们之间的联系依靠的是一个基本的事件消息机制来互相联系

Web Worker之间的数据传递

Worker与网页通过事件接口来通信

Web Worker的使用场景与浏览器的支持比较有限,就现在来说,常用的场景还是在处理比较大的数据,或者处理与UI无关的长时间运行脚本,比如转换大量的字符串啦,大数据集的搜索排序之类啦。

页面元素的响应处理

这个的话其实主要是从用户体验的角度来“欺骗性的”提高用户对网站速度的印象,怎么说呢

当用户与程序交互的时候,程序要立即给出响应

核心就是上面这句话,立即给出响应不表示你的程序要立即处理完任务,而是要立即做出一个反馈,这个反馈一般会是改变触发交互元素的外观,最常见的就是给可交互按钮添加各种状态,未被触发、触发中、触发后最好都要有个外观变化,这对用户来说是最直观的,无论触发交互的时候任务是否完成,都要立即给予对应的状态来给用户知道我的操作成功了,正在处理,处理好了,不给予反馈的交互过程很容易会让用户认为出了什么问题。

上一篇下一篇

猜你喜欢

热点阅读