高级技巧(3)——JS高级定时器

高级技巧(3)——JS高级定时器

  • 先是自己总结,课本细知识点在后面

自己总结

  • 先思考:

    • 1、定时器真的是定时执行的吗
      • 定时器不能保证真正定时执行
      • 一般会延迟一点(可以接受),也有可能延迟很长时间(不能接受)
    • 2、定时器回调函数是在哪个线程执行的?
      • 在主线程执行,js是单线程的
      • 扩展:在这里插入图片描述
        在这里插入图片描述
  • 3、定时器是如何实现的?

    • 事件循环模型

在这里插入图片描述

  • 第一个问题可以用一个简单的代码段测试一下:

<body>
    <button id="btn">启动定时器</button>
    <script>
        document.getElementById("btn").onclick = function () {
            var start = Date.now();
            console.log("启动定时器前");
            setTimeout(function(){
                console.log("定时器执行了:",Date.now()-start)
            },200);
            console.log("启动定时器以后");
        };


    </script>
</body>
  • 结果:
    在这里插入图片描述

  • 1、其实上面还可以在内部写个循环次数较大的循环,就不截图了,发现时间会延迟到580多秒甚至更多(根据循环而定)

  • 2、无论是回调函数还是非回调函数都是在主线程执行的,可以把过程看成初始化代码+回调代码

  • 3、要注意时间间隔并不是代表实际执行代码的时间,而是说何时将定时器的代码添加到事件队列当中

课本细解

初解高级定时器

浏览器是负责排序,指派某段代码在某个时间点的优先级

  • 时间线(单线程你可以把js想象成是在时间线上执行的)
    在这里插入图片描述

    • 注意点:1、随着页面在其生命周期中的推移,会按照执行顺序添加进入队列,回调函数也是这样,接收到某个Ajax响应后,回调函数的代码会被添加到队列之中
    • 2、而定时器对代码队列的处理在上面自己总结里面最后一点提到了
  • 扩展:firefox定时器扩展功能:能确定定时器过多久才执行的

    • 例子:
//仅 Firefox 中
setTimeout(function(diff){
if (diff > 0) {
//晚调用
} else if (diff < 0){
//早调用
} else {
//调用及时
}
}, 250);
重复的定时器

在之前的博客中已经提到了时间处理函数,这里再复习一波

  • 复习
    • 1、setTimeout(fn,t),超时调用,超过时间t,就执行fn。
    • 2、setInterval(fn,t),间歇调用,调用周期t,执行fn。
    • 3、二者调用后,均返回一个数值ID,此ID是计划执行代码的唯一标识符,可以通过它来取消尚未执行的调用。
    • 4、 clearTimeout(id)和clearInterval(id)。取消间歇调用的重要性要远远高于取消超时调用,因为在不加干涉的情况下,间歇调用将会一直执行到页面卸载。
  • setInterval()
    • 使用:当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中,这确保了定时器代码加入到队列中的最小时间间隔为指定间隔
    • 问题:
      • 某些间隔会被跳过
      • 多个定时器的代码执行之间的间隔可能会比预期的小

用 setInterval()为onclick事件设置一个重复定时器,如果定时器代码和事件处理程序花了差不多的时间,那么就会出现跳过间隔连续运行定时器代码的情况

  • 解决:使用链式 setTimeout()
    调用:
setTimeout(function(){
//处理中
setTimeout(arguments.callee, interval);
}, interval);

复习arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数

  • 对解决方式的解析
    • 1、在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,因此就不会有缺失的间隔
    • 2、可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,这样也就避免了连续执行的问题
  • 一个运用解决方式的例子:
setTimeout(function(){
var div = document.getElementById("myDiv");
left = parseInt(div.style.left) + 5;
div.style.left = left + "px";
if (left < 200){
setTimeout(arguments.callee, 50);
}
}, 50);

代码解析:将div元素向右移动,左坐标在200像素的事后停止(很像之前canvas做的盒子来回移动小动画的简单实现,也是依靠坐标执行),在js动画中这样设置时间间隔就可以使用这个解决办法的模式

数组分块技术
  • 基本思路
    • 1、为要处理的项目创建一个队列
    • 2、使用定时器取出下一个要处理的项目进行处理
    • 3、设置另一个定时器
  • 基本模式
setTimeout(function(){
//取出下一个条目并处理
var item = array.shift();
process(item);
//若还有条目,再设置另一个定时器
if(array.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100);

解析:array 这个变量本质上就是一个“待办事宜”列表,它包含了要处理的项目

  • 扩展(shift()方法)
    在这里插入图片描述

在上面例子里面shift可以用来获取队列中下一个要处理的项目,然后将其传递给某个函数

  • 函数实现:
//三个参数:要处理的项目的数组,用于处理项目的函数,以及可选的运行该函数
的环境
function chunk(array, process, context){
setTimeout(function(){
var item = array.shift();
process.call(context, item);
if (array.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100);
}

解析
1、上面那个时间间隔100ms可以根据需要自己去更改,但是课本说这个时间是效果很好的
2、参数中那个环境参数当环境是全局环境的时候其实是不需要的

  • 使用(例子代码是用 printValue() 函数将 data 数组中的每个值输出到一个
    元素)
var data = [12,123,1234,453,436,23,23,5,4123,45,346,5634,2234,345,342];
function printValue(item){
var div = document.getElementById("myDiv");
div.innerHTML += item + "<br>";
}
chunk(data, printValue);
  • 最后一个小点

一旦某个函数需要花 50ms 以上的时间完成,那么最好看看能否将任务分割为一系列可以使用定时器的小任务——摘自《JavaScript高级程序

猜你喜欢

转载自blog.csdn.net/Phoebe4/article/details/107623298