setInterval和setTimeout的缺陷和优势分析

先把问题摆出来:
使用定时器的setInterval()方法会出现程序并不是按照我们设定的精确时间而调用的问题!

定时器:

在JavaScript中经常会使用定时器来进行延时或者是重复调用的操作;定时器有两个方法:
1:setInterval(参数1,参数2);
参数1:要执行的代码,可以为function();
参数2:间隔时间 单位ms;
执行无限次直到取消定时器。

2:setTimeout(参数1,参数2);
参数1:要执行的代码,可以为function();
参数2:延迟时间 单位ms;
执行一次。

注意区分:setInterval()的参数2是间隔时间,setTimeout()的参数2是延迟时间;
这一段将是接下来进行二者详细区分的重要根据;

一:setInterval() 辉煌下隐藏的破败##

实例1

var num = 0;
    var test1 =  setInterval(function (){
        num++;
        var data = new Date();
        var str = data.getHours() + ":" + data.getMinutes() + ":" + data.getSeconds() + ":" + data.getMilliseconds();
        console.log(str);
        if (num > 5) {
            clearInterval(test1);
        }
    },1000);

1:代码;程序设置定时器每1秒执行一次,当执行次数为六次时消掉计数器;
2:从运行结果上来看,在精确到毫秒之后就有些许的波动;
实例2

var num2 = 0;
    var test2 = setInterval(function(){
        num2++;
        for(var i = 0;i < 500000;i++){
            var div = document.createElement("div");
            var container = document.getElementById("d1");
            container.appendChild(div);
        }
        var data = new Date();
        var str = data.getHours() + ":" + data.getMinutes() + ":" + data.getSeconds() + ":" + data.getMilliseconds();
        console.log(str);
        if(num2 > 5){
            clearInterval(test2);
        }
    },1000);

1:代码描述:定时器中有一个50万次的循环,创建和追加div元素这个操作是非常耗费性能的,运行一次我这破机子瞬间不好了;执行完一次for循环之后就输出一次当前的时间,运行六次就消除定时器。
2:执行结果:如图所示每一次执行定时器的操作的时间相差分别是7,9,9,12,13秒,
哎!!定义的不是每一秒执行一次吗?为什么出现相差 这么多秒!!!!???
此时请出在开头亮出来的问题:
setInterval()方法会出现程序并不是按照我们设定的精确时间而调用的问题!!!;
这是为什么?我设置了程序调用时间为什么又不按照我说的办了??

简单地画了个图,描述一下程序的执行时间为什么会产生错乱。
图一:
这里写图片描述

这张图描述的是当A <= B时的调用状况。此时程序每一次的执行根本就不会影响到定时器设置的间隔调用;所以此时会产生实例1中输出的时间是严格按照我们所设定的精确时间进行调用;

图二:
这里写图片描述
曾经我以为使用定时器的函数,只要到达了设置的间隔时间,即使他没有执行完也会被定时器强行的从栈中扯出来从头开始执行,这样从头到尾就没有一次是完整的执行出来的;这TM怎么可能!!!!实例2就已经实力打脸了!

真正的执行是这样的---------图三:

你看,这每一个圆圈代表每一次的程序执行时间A,底下那各自连接不上的憋屈红线就是定时器设置的间隔时间B,和图二是截然不同的,只有在函数每一次执行完之后才会调用定时器,继续重新进行调用;卧槽?这是为啥?这才是导致开头问题的原因!!!
所以我把这一节称之为辉煌下的破败

重点原理:

JavaScript是运行在单线程的环境中的,所以这就意味着定时器就成了要执行的计划!而不是必须要执行的铁律! 为啥呢? 当函数开始执行时在栈中创建出来一个栈帧,这个栈帧的执行是需要时间的,假设有3秒,在这三秒内,JavaScript的单线程特点就会确保在这3秒内全力的专一的去解决掉这个栈帧(函数)。所以在这个函数运行的时候定时器是没有能力终止他的运行的,因此当函数的运行时间大于间隔时间时,间隔时间1秒到了,但是程序还有2秒没有执行完,那你也给我老老实实的等着函数执行完!!!!

二:setTimeout() 平凡的力量##

奇坑无比的setInterval()有着看似无法解决的弊端;
据说有一种解决思路就是将间隔时间设置的大点,远大于函数执行的时间。。。定时器说了,老子管不了你还不能装看不见吗? 眼不见心不烦。。。
乍一看,确实有道理,但是这也不大合适,就像前面实例2中,每一次执行同一个函数的执行时间都是有些区别的。你该怎么去设置这个间隔时间呢?设置的大点?从实际上出发是不合适,因此果断抛弃。

在setTimeout()中,他的参数2代表的是延迟时间,他是先延迟后执行,setInterval()是先执行,后使用时间间隔;无限次执行;

原理图:
这里写图片描述
如图先有一段延迟时间,之后再执行函数方法;可以看到参数2 的延迟时间对函数的执行是毫无影响的;此时如果把setTimeout放到循环体中,那么是不是代表这就是一个秩序井然的程序。既可以达到setInterval的循环执行的效果,又解决了setInterval的时间不精确的问题;

var i=0;
    function show() {
        console.log(i);
        i++;
        if(i<5){
            setTimeout(show,1000);
        }
    }
    show();

停止超时调用方法:

这是一段骚气的代码,如此造成循环每隔1秒就输出i;


总结
1:setInterval的参数2是间隔时间;
2:setTimeout的参数2是延迟时间;
3:setInterval受单线程影响出现时间不精确现象;
4:使用循环+setTimeout可以完成循环执行,并且弥补了setInterval的不足;

原文地址:https://www.cnblogs.com/BluceLee/p/13749706.html