Js中setTimeout()和setInterval() 何时被调用执行的用法


Posted in Javascript onApril 12, 2013

定义
setTimeout()和setInterval()经常被用来处理延时和定时任务。setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式,而setInterval()则可以在每隔指定的毫秒数循环调用函数或表达式,直到clearInterval把它清除。
从定义上我们可以看到两个函数十分类似,只不过前者执行一次,而后者可以执行多次,两个函数的参数也相同,第一个参数是要执行的code或句柄,第二个是延迟的毫秒数。
很简单的定义,使用起来也很简单,但有时候我们的代码并不是按照我们的想象精确时间被调用的,很让人困惑

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

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秒记录一次当前时间(分钟:秒:毫秒),记录十次后清除,不再记录。考虑到代码执行时间可能记录的不是执行时间,但时间间隔应该是一样的,看看结果
:38:116
:39:130
:40:144
:41:158
:42:172
:43:186
:44:200
:45:214
:46:228
:47:242
:48:256

为什么
时间间隔几乎是1000毫秒,但不精确,这是为什么呢?原因在于我们对JavaScript定时器存在一个误解,JavaScript其实是运行在单线程的环境中的,这就意味着定时器仅仅是计划代码在未来的某个时间执行,而具体执行时机是不能保证的,因为页面的生命周期中,不同时间可能有其他代码在控制JavaScript进程。在页面下载完成后代码的运行、事件处理程序、Ajax回调函数都是使用同样的线程,实际上浏览器负责进行排序,指派某段程序在某个时间点运行的优先级。
我们把效果放大一下看看,添加一个耗时的任务
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);

我们又加入了一个定时任务,看看结果
:9:222
:12:482
:16:8
:19:143
:22:631
:25:888
:28:712
:32:381
:34:146
:35:565
: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的时候停止。
Javascript 相关文章推荐
jquery构造器的实现代码小结
May 16 Javascript
javascript实现英文首字母大写
Apr 23 Javascript
Node.js中使用jQuery的做法
Aug 17 Javascript
完美实现js焦点轮播效果(一)
Mar 07 Javascript
Angular组件化管理实现方法分析
Mar 17 Javascript
JS 组件系列之Bootstrap Table的冻结列功能彻底解决高度问题
Jun 30 Javascript
微信小程序修改swiper默认指示器样式的实例代码
Jul 18 Javascript
微信小程序实现留言板
Oct 31 Javascript
详解CommonJS和ES6模块循环加载处理的区别
Dec 26 Javascript
JQuery实现ul中添加LI和删除指定的Li元素功能完整示例
Oct 16 jQuery
前端vue-cli项目中使用img图片和background背景图的几种方法
Nov 13 Javascript
Vue 构造选项 - 进阶使用说明
Aug 14 Javascript
js实现单一html页面两套css切换代码
Apr 11 #Javascript
获取内联和链接中的样式(js代码)
Apr 11 #Javascript
JavaScript在XHTML中的用法详解
Apr 11 #Javascript
JavaScript中的noscript元素属性位置及作用介绍
Apr 11 #Javascript
javascript标签在页面中的位置探讨
Apr 11 #Javascript
JS添加删除一组文本框并对输入信息加以验证判断其正确性
Apr 11 #Javascript
如何使用json在前后台进行数据传输实例介绍
Apr 11 #Javascript
You might like
PHP中在数据库中保存Checkbox数据(2)
2006/10/09 PHP
PHP版自动生成文章摘要
2008/07/23 PHP
简单的pgsql pdo php操作类实现代码
2016/08/25 PHP
Zend Framework入门教程之Zend_Mail用法示例
2016/12/08 PHP
laravel 获取当前url的别名方法
2019/10/11 PHP
向fckeditor编辑器插入指定代码的方法
2007/05/25 Javascript
九种js弹出对话框的方法总结
2013/03/12 Javascript
Jquery中使用setInterval和setTimeout的方法
2013/04/08 Javascript
JS控制图片翻转示例代码(兼容firefox,ie,chrome)
2013/12/19 Javascript
JS案例分享之金额小写转大写
2014/05/15 Javascript
原生JS实现平滑回到顶部组件
2016/03/16 Javascript
原生JS实现旋转木马式图片轮播插件
2016/04/25 Javascript
three.js绘制地球、飞机与轨迹的效果示例
2017/02/28 Javascript
详解AngularJs ui-router 路由的简单介绍
2017/04/26 Javascript
解决vue attr取不到属性值的问题
2018/09/18 Javascript
jQuery实现的鼠标拖动浮层功能示例【拖动div等任何标签】
2018/12/29 jQuery
基于vue实现探探滑动组件功能
2020/05/29 Javascript
js屏蔽F12审查元素,禁止修改页面代码等实现代码
2020/10/02 Javascript
python django集成cas验证系统
2014/07/14 Python
Python实现查找系统盘中需要找的字符
2015/07/14 Python
深入理解python try异常处理机制
2016/06/01 Python
python 解决动态的定义变量名,并给其赋值的方法(大数据处理)
2018/11/10 Python
Python OpenCV调用摄像头检测人脸并截图
2020/08/20 Python
python爬取王者荣耀全皮肤的简单实现代码
2020/01/31 Python
Python使用sys.exc_info()方法获取异常信息
2020/07/23 Python
Pandas中DataFrame交换列顺序的方法实现
2020/12/14 Python
HTML5 source标签:媒介元素定义媒介资源
2018/01/29 HTML / CSS
美国存储和组织商店:The Container Store
2017/08/16 全球购物
Ben Sherman官方网站:英国男装品牌
2019/10/22 全球购物
五十岁生日宴会答谢词
2014/01/15 职场文书
文明城市创建标语
2014/06/16 职场文书
酒店保洁员岗位职责
2015/02/26 职场文书
2016年“七一建党节”广播稿
2015/12/18 职场文书
2019关于垃圾分类处理的调查报告
2019/12/26 职场文书
使用Pytorch训练two-head网络的操作
2021/05/28 Python
springboot集成springCloud中gateway时启动报错的解决
2021/07/16 Java/Android