title: 高级定时器
date: 2016-12-13
tag: JS高级技巧css
首先,JavaScript 中没有代码是当即执行的,而是一旦进程空闲则当即执行。html
进程什么时候空闲,取决于上一个执行队列的执行时间,而与此对应的是随着页面中生命周期的推移而产生的代码执行顺序队列。数组
定时器对队列的工做方式是,当设定的时间过去之后将代码插入队列,但不表明代码会被当即执行。浏览器
不少状况下,咱们都须要使用 setInterval() 重复的执行同一段代码去作同一件事情,而在这时,最大的问题在于定时器可能在代码再次被添加到队列以前尚未被执行完成,从而致使某些间隔被跳过或者多个定时器的代码执行时间间隔被缩短。异步
为了不以上缺点,可使用链式调用 setTimeout() 模式函数
setTimeout(function(){ // do something setTimeout(arguments.callee, interval); }, interval)
一个例子:code
setTimeout(function(){ $("#block").css({ 'left': $('#block').position().left -1, }) if($('#block').position().left > 0){ setTimeout(arguments.callee, 30); } }, 30)
为了防止恶意程序猿将用户的计算机搞挂,浏览器对 JavaScript 可以使用的资源进行了限制,若是代码的运行时间超过特定时间或者特定语句数量就不让其继续运行。htm
而脚本运行时间过长的两个主要缘由是:1)过长,过深嵌套的函数调用;2)进行大量处理的循环。对象
针对第二种问题,使用定时器是解决方法之一。使用定时器分隔循环,是一种叫做 数组分块(array chunking) 的技术。生命周期
在数组分块模式中,array 变量本质上就是一个 “代办事项” 列表,它包含了要处理的项目,而 shift() 能够获取队列中下一个要处理的项目,而后将其传递个某个函数。当队列中还剩下其它项目时,则设置另外一个定时器,并经过 arguments.callee 调用同一个匿名函数。
function chunk(array, process, context){ setTimeout(function(){ var item = array.shift() process.call(context, item) if(array.length > 0){ setTimeout(arguments.callee, 100) } }, 100) }
chunk() 方法接收三个参数: 要处理项目的数组,用于处理项目的函数,可选的运行该函数的环境。
在函数内部,经过 call() 调用 process() 函数,这样能够设置一个合适的执行环境。为定时器设定的时间间隔使得 JavaScript 进程有时间在处理项目的事件之间转入空闲。
调用实例:
var data = [12,124,343,56,76767,43,654,34645,56456,767,4645] function printValue(item){ var div = $('#block').html() $('#block').html(div + item + '<br>') } chunk(data, printValue)
如上,函数 printValue()
将 data
数组中的每一个值输出到一个 div
元素中。因为函数处于全局做用域中,所以无需给 chunk()
函数传递 context
对象。
若是想保持原数组不变,则应将该数组的克隆传递给 chunk()
chunk(data.concat(), printValue)
调用某个数组的.contact()
,若是不传递任何参数,将返回和原来数组中项目同样的数组。
函数节流 的基本思想是指,某些代码不能够在没有间断的状况下连续重复的执行。
浏览器中某些计算和处理的代价要比其余的昂贵不少,好比,DOM 操做比非 DOM 交互须要更多的内存和 CPI 时间,而进行过多的 DOM 相关操做可能致使浏览器挂起甚至崩溃,对于这种问题,可使用定时器对函数进行节流。
函数节流的基本模式能够简化以下:
function throttle(method, context){ clearTimeout(method.tId) method.tId = setTimeout(function(){ method.call(context) }, 100) }
throttle() 函数接收两个参数: 要执行的函数以及在哪一个做用域中执行。该函数首先清除以前设置的任何定时器。定时器 ID 是存储在函数的 tId
属性中的,固然,首次将方法传递给 throttle 函数可能并不存在该属性。而后定义一个新的定时器,并将 ID 存储在 tId 属性中。而 call() 用来确保方法在适当的环境中执行。若是没有给出第二个参数,那么就在全局做用域内执行该方法。
在 setTimeout() 中用到的函数其执行环境老是 window
throttle 方法调用实例:
function resizeDiv(){ var div = document.querySelector("#block") div.style.height = div.offsetWidth + "px" } window.onresize = function(){ throttle(resizeDiv) }
如上,为了保证在 resize
事件中浏览不会进行高频率,或者屡次计算,咱们给 window.onresize
绑定了一个函数,在该函数调用了 throttle 方法,从而在窗口大小发生改变的时候是 div#block
的高度与其宽度保持一致。