摘要:高级定时器高级技巧异步首先,中没有代码是立即执行的,而是一旦进程空闲则立即执行。针对第二种问题,使用定时器是解决方法之一。为定时器设定的时间间隔使得进程有时间在处理项目的事件之间转入空闲。该函数首先清除之前设置的任何定时器。
title: 高级定时器
date: 2016-12-13
tag: JS高级技巧
首先,JavaScript 中没有代码是立即执行的,而是一旦进程空闲则立即执行。
进程何时空闲,取决于上一个执行队列的执行时间,而与此对应的是随着页面中生命周期的推移而产生的代码执行顺序队列。
定时器对队列的工作方式是,当设定的时间过去以后将代码插入队列,但不代表代码会被立即执行。
0x01 重复定时器很多情况下,我们都需要使用 setInterval() 重复的执行同一段代码去做同一件事情,而在这时,最大的问题在于定时器可能在代码再次被添加到队列之前还没有被执行完成,从而导致某些间隔被跳过或者多个定时器的代码执行时间间隔被缩短。
为了避免以上缺点,可以使用链式调用 setTimeout() 模式
setTimeout(function(){ // do something setTimeout(arguments.callee, interval); }, interval)
一个例子:
setTimeout(function(){ $("#block").css({ "left": $("#block").position().left -1, }) if($("#block").position().left > 0){ setTimeout(arguments.callee, 30); } }, 30)0x01 数组分块
为了防止恶意程序猿将用户的计算机搞挂,浏览器对 JavaScript 能够使用的资源进行了限制,如果代码的运行时间超过特定时间或者特定语句数量就不让其继续运行。
而脚本运行时间过长的两个主要原因是: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 + "
") } chunk(data, printValue)
如上,函数 printValue() 将 data 数组中的每个值输出到一个 div 元素中。由于函数处于全局作用域中,因此无需给 chunk() 函数传递 context 对象。
如果想保持原数组不变,则应将该数组的克隆传递给 chunk()
chunk(data.concat(), printValue)
调用某个数组的.contact(),如果不传递任何参数,将返回和原来数组中项目一样的数组。
0x02 函数节流函数节流 的基本思想是指,某些代码不可以在没有间断的情况下连续重复的执行。
浏览器中某些计算和处理的代价要比其他的昂贵很多,比如,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 的高度与其宽度保持一致。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/91349.html
摘要:图二解读定时器可以在指定时间把定时器代码加入待执行队列,但并不能保证代码执行时机,待执行队列中的代码要等进程空闲时才能执行。也就是说定时器每隔间隔触发一次,尝试加入队列,拥堵时段将直接忽略本次操作。 图片出自JS高程(第三版) showImg(https://segmentfault.com/img/bVbgC3V?w=1337&h=313); 图一解读:JS运行于单线程的环境中:页面...
摘要:关于定时器要记住的最重要的事情是指定的时间间隔表示何时将定时器的代码添加到队列,而不是何时实际执行代码。多个定时器之间的执行间隔会比预期的小解决办法处理中数组分块,,函数节流,实际进行处理的方法实际执行的代码初始处理调用的方法 一、高级函数 安全类型检测 Object.protitype.toString.call(value) 作用域安全的构造函数 function Pers...
摘要:下面通过几个的定时器示例以及相关源码来分析在中,功能到底是怎么实现的。我们知道,中的定时器并不同于计算机底层的定时中断。补充资料在高级程序设计第三版第章高级技巧中对高级定时器以及有较详细的讨论。至此,这类定时器函数已经可以为所用了。 上一篇博文提到,在Node中timer并不是通过新开线程来实现的,而是直接在event loop中完成。下面通过几个JavaScript的定时器示例以及N...
摘要:和的定义是指多少时间之后将回调函数加入到的执行队列之中回调函数是否立即执行取决于当前的执行队列是否空闲。比较好的例子如下回调函数执行其他操作假如内部的执行时间为那么的回调函数至少要等待才执行。 1、惰性加载函数 (判断各个浏览器中是否支持某个属性) function addEvent(elem, type, handler){ if(elem.addEventListener...
摘要:闭包闭包是指有权访问另一个函数作用域中的变量的函数当某个函数被调用时,会创建一个执行环境及相应的作用域链。要注意通过第句声明的这个方法属于构造函数生成的对象,而不属于构造函数的变量对象,也就是说,并不存在于作用域链中。 看到评论里有仁兄建议我试试箭头函数,真是受宠若惊,本来写这篇文章也只是想记录写要点给自己日后看的。今天早上看到一篇总结javascript中this的文章JavaScr...
阅读 747·2021-10-14 09:43
阅读 2071·2021-09-30 09:48
阅读 3440·2021-09-08 09:45
阅读 1088·2021-09-02 15:41
阅读 1876·2021-08-26 14:15
阅读 769·2021-08-03 14:04
阅读 2970·2019-08-30 15:56
阅读 3070·2019-08-30 15:52