setTimeout()和setInterval() 什么时候被调用执行

定义

setTimeout()和setInterval()常常被用来处理延时和定时任务。setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式,而setInterval()则能够在每隔指定的毫秒数循环调用函数或表达式,直到clearInterval把它清除。浏览器

从定义上咱们能够看到两个函数十分相似,只不过前者执行一次,然后者能够执行屡次,两个函数的参数也相同,第一个参数是要执行的code或句柄,第二个是延迟的毫秒数。app

很简单的定义,使用起来也很简单,但有时候咱们的代码并非按照咱们的想象精确时间被调用的,很让人困惑函数

简单示例

看个简单的例子,简单页面在加载完两秒后,写下Delayed alert!spa

setTimeout('document.write("Delayed alert!");', 2000);

看起来很合理,咱们再看个setInterVal()方法的例子线程

复制代码
var num = 0;
        var i = setInterval(function() {
            num++;
            var date = new Date();
            document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
            if (num > 10)
                clearInterval(i);
        }, 1000);
复制代码

页面每隔1秒记录一次当前时间(分钟:秒:毫秒),记录十次后清除,再也不记录。考虑到代码执行时间可能记录的不是执行时间,但时间间隔应该是同样的,看看结果code

复制代码
43:38:116
43:39:130
43:40:144
43:41:158
43:42:172
43:43:186
43:44:200
43:45:214
43:46:228
43:47:242
43:48:256
复制代码

 

为何

时间间隔几乎是1000毫秒,但不精确,这是为何呢?缘由在于咱们对JavaScript定时器存在一个误解,JavaScript实际上是运行在单线程的环境中的,这就意味着定时器仅仅是计划代码在将来的某个时间执行,而具体执行时机是不能保证的,由于页面的生命周期中,不一样时间可能有其余代码在控制JavaScript进程。在页面下载完成后代码的运行、事件处理程序、Ajax回调函数都是使用一样的线程,实际上浏览器负责进行排序,指派某段程序在某个时间点运行的优先级blog

咱们把效果放大一下看看,添加一个耗时的任务排序

复制代码
function test() {
            for (var i = 0; i < 500000; i++) {
                var div = document.createElement('div');
                div.setAttribute('id', 'testDiv');
                document.body.appendChild(div);
                document.body.removeChild(div);
            }
        }
        setInterval(test, 10);
        var num = 0;
        var i = setInterval(function() {
            num++;
            var date = new Date();
            document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
            if (num > 10)
                clearInterval(i);
        }, 1000);
复制代码

咱们又加入了一个定时任务,看看结果生命周期

复制代码
47:9:222
47:12:482
47:16:8
47:19:143
47:22:631
47:25:888
47:28:712
47:32:381
47:34:146
47:35:565
47:37:406
复制代码

这下效果明显了,差距甚至都超过了3秒,并且差距很不一致。队列

咱们能够能够把JavaScript想象成在时间线上运行。当页面载入的时候首先执行的是页面生命周期后面要用的方法和变量声明和数据处理,在这以后JavaScript进程将等待更多代码执行。当进程空闲的时候,下一段代码会被触发

除了主JavaScript进程外,还须要一个在进程下一次空闲时执行的代码队列。随着页面生命周期推移,代码会按照执行顺序添加入队列,例如当按钮被按下的时候他的事件处理程序会被添加到队列中,并在下一个可能时间内执行。在接到某个Ajax响应时,回调函数的代码会被添加到队列。JavaScript中没有任何代码是当即执行的,但一旦进程空闲则尽快执行。定时器对队列的工做方式是当特定时间过去后将代码插入,这并不意味着它会立刻执行,只能表示它尽快执行。

知道了这些后,咱们就能明白,若是想要精确的时间控制,是不能依赖于JavaScript的setTimeout函数的。

重复的定时器

使用 setInterval() 建立的定时器可使代码循环执行,到有指定效果的时候,清除interval就能够,以下例

复制代码
var my_interval = setInterval(function () {
            if (condition) {
                //..........
            } else {
                clearInterval(my_interval);
            }
        }, 100);
复制代码

但这个方式的问题在于定时器的代码可能在代码再次被添加到队列以前尚未执行完成,结果致使循环内的判断条件不许确,代码多执行几回,之间没有停顿。不过JavaScript已经解决这个问题,当使用setInterval()时,仅当没有该定时器的其余代码实例时才将定时器代码插入队列。这样确保了定时器代码加入到队列的最小时间间隔为指定间隔。

这样的规则带来两个问题

  1. 1. 某些间隔会被跳过
  2. 2.多个定时器的代码执行之间的间隔可能比预期要小

为了不这两个缺点,咱们可使用setTimeout()来实现重复的定时器

setTimeout(function () {
            //code
            setTimeout(arguments.callee, interval);
        }, interval)

这样每次函数执行的时候都会建立一个新的定时器,第二个setTimeout()调用使用了agrument.callee 来获取当前实行函数的引用,并设置另一个新定时器。这样作能够保证在代码执行完成前不会有新的定时器插入,而且下一次定时器代码执行以前至少要间隔指定时间,避免连续运行。

复制代码
setTimeout(function () {
            var div = document.getElementById('moveDiv');
            var left = parseInt(div.style.left) + 5;
            div.style.left = left + 'px';
            if (left < 200) {
                setTimeout(arguments.callee, 50);
            }
        }, 50);
复制代码

这段定时器代码每次执行的时候,把一个div向右移动5px,当坐标大于200的时候中止。

相关文章
相关标签/搜索