javascript 定时器工作原理
2015-11-10 15:26
417 查看
说到 javascript 中的定时器,我们肯定会想到
Loop) 的角度来分析两者的工作原理和区别。
MDN对
在指定的延迟时间之后调用一个函数或执行一个代码片段。
IE0+ 还支持回调参数的传入:
MDN 对 setInterval 的定义为:
周期性地调用一个函数(function)或者执行一段代码。
由于
由于javascript 的事件循环机制,导致第二个参数并不代表延迟delay毫秒之后立即执行回调函数,而是尝试将回调函数加入到事件队列。实际上,
setTimeout:延时delay毫秒之后,啥也不管,直接将回调函数加入事件队列。
setInterval: 延时delay毫秒之后,先看看事件队列中是否存在还没有执行的回调函数(setInterval的回调函数),如果存在,就不要再往事件队列里加入回调函数了。
所以,当我们的代码中存在耗时的任务时,定时器并不会表现的如我们所想的那样。
下面的代码,本来希望能够在
这样的结果看上去正是我们所想的那样,但是一旦我们在代码中加入了耗时的任务时候,结果就不像我们所期望的那样了:
两个
首先,第一个耗时任务(
从耗时任务开始执行,过了
第一个
这个定时器也是期望延时
1000ms 才开始执行。
可以用下面的图来概括:
再来看
上面这段代码,我们期望每隔
不然,会出现多个回调同时执行的情况。
可以用下面的图来概括:
上面对javascript定时器执行原理进行了简要的分析,希望能够帮助我们更深入的理解javascript。文中有描述不当的地方可以在评论中指出。
文章地址:http://blog.mcbird.cn/2015/09/18/javascript-timers/
setTimeout()和
setInterval()这两个函数。本文将从 事件循环(Event
Loop) 的角度来分析两者的工作原理和区别。
setTimeout()
MDN对 setTimeout的定义为:
在指定的延迟时间之后调用一个函数或执行一个代码片段。
语法
setTimeout的语法非常简单,第一个参数为回调函数,第二个参数为延时的时间。函数返回一个数值类型的ID唯一标示符,此ID可以用作
clearTimeout的参数来取消定时器:
var timeoutID = window.setTimeout(code, delay);
IE0+ 还支持回调参数的传入:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
setInterval()
MDN 对 setInterval 的定义为:周期性地调用一个函数(function)或者执行一段代码。
由于
setInterval和
setTimeout的用法一样,所以这里不再列出。
对第二个参数(delay)的说明
由于javascript 的事件循环机制,导致第二个参数并不代表延迟delay毫秒之后立即执行回调函数,而是尝试将回调函数加入到事件队列。实际上,setTimeout和
setInterval在这一点上处理又存在区别:
setTimeout:延时delay毫秒之后,啥也不管,直接将回调函数加入事件队列。
setInterval: 延时delay毫秒之后,先看看事件队列中是否存在还没有执行的回调函数(setInterval的回调函数),如果存在,就不要再往事件队列里加入回调函数了。
所以,当我们的代码中存在耗时的任务时,定时器并不会表现的如我们所想的那样。
通过一个例子来理解
下面的代码,本来希望能够在 100ms和
200ms的时候(也就是刚好等待
100ms)调用回调函数:
var timerStart1 = now(); setTimeout(function () { console.log('第一个setTimeout回调执行等待时间:', now() - timerStart1); var timerStart2 = now(); setTimeout(function () { console.log('第二个setTimeout回调执行等待时间:', now() - timerStart2); }, 100); }, 100); // 输出: // 第一个setTimeout回调执行等待时间: 106 // 第二个setTimeout回调执行等待时间: 107
这样的结果看上去正是我们所想的那样,但是一旦我们在代码中加入了耗时的任务时候,结果就不像我们所期望的那样了:
var timerStart1 = now(); setTimeout(function () { console.log('第一个setTimeout回调执行等待时间:', now() - timerStart1); var timerStart2 = now(); setTimeout(function () { console.log('第二个setTimeout回调执行等待时间:', now() - timerStart2); }, 100); heavyTask(); // 耗时任务 }, 100); var loopStart = now(); heavyTask(); // 耗时任务 console.log('heavyTask耗费时间:', now() - loopStart); function heavyTask() { var s = now(); while(now() - s < 1000) { } } function now () { return new Date(); } // 输出: // heavyTask耗费时间: 1015 // 第一个setTimeout回调执行等待时间: 1018 // 第二个setTimeout回调执行等待时间: 1000
两个
setTimeout的等待事件由于耗时任务的存在不再是
100ms了!我们来描述一下事情的经过:
首先,第一个耗时任务(
heavyTask())开始执行,它需要大约
1000ms才能执行完毕。
从耗时任务开始执行,过了
100ms, 第一个
setTimeout的回调函数期望执行,于是被加入到事件队列,但是此时前面的耗时任务还没执行完,所以它只能在队列中等待,直到耗时任务执行完毕它才开始执行,所以结果中我们开的看到的是:
第一个setTimeout回调执行等待时间: 1018。
第一个
setTimeout回调一执行,又开启了第二个
setTimeout,
这个定时器也是期望延时
100ms之后能够执行它的回调函数。 但是,在第一个
setTimeout又存在一个耗时任务,所有它的剧情跟第一个定时器一样,也等待了
1000ms 才开始执行。
可以用下面的图来概括:
再来看
setInterval的一个例子:
var intervalStart = now(); setInterval(function () { console.log('interval距定义定时器的时间:', now() - loopStart); }, 100); var loopStart = now(); heavyTask(); console.log('heavyTask耗费时间:', now() - loopStart); function heavyTask() { var s = now(); while(now() - s < 1000) { } } function now () { return new Date(); } // 输出: // heavyTask耗费时间: 1013 // interval距定义定时器的时间: 1016 // interval距定义定时器的时间: 1123 // interval距定义定时器的时间: 1224
上面这段代码,我们期望每隔
100ms就打出一条日志。相对于
setTimeout的区别,
setInterval在准备把回调函数加入到事件队列的时候,会判断队列中是否还有未执行的回调,如果有的话,它就不会再往队列中添加回调函数。
不然,会出现多个回调同时执行的情况。
可以用下面的图来概括:
总结
上面对javascript定时器执行原理进行了简要的分析,希望能够帮助我们更深入的理解javascript。文中有描述不当的地方可以在评论中指出。文章地址:http://blog.mcbird.cn/2015/09/18/javascript-timers/
相关文章推荐
- JavaScript的document方法汇总
- 【Extjs】timefield使用,以及24小时制
- 简单学习JavaScript中的for语句循环结构
- js密码强度校验
- 详解javascript函数的参数
- JavaScript学习(二):原始数据类型-字符串、数字、布尔值、null、undefined
- JSON对象长度和遍历方法
- JavaScript算法详解——快速排序
- Ajax&JSON的简单使用
- JSON.stringify转换Date不正确的解決方法
- JavaScript函数的一些注意要点小结及js匿名函数
- javascript实现五星评分功能
- Javascript设计模式理论与实战:工厂方法模式
- androids-addjavascriptinterface-equivalent-in-ios
- 跨子域的iframe高度自适应
- JS定时执行,循环执行
- javascript密码验证
- 修改jsp的默认的编码方式
- JavaScript 各种遍历方式详解
- json_encode和json_decode的用法