说到 javascript 中的定时器,我们肯定会想到 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。文中有描述不当的地方可以在评论中指出。
热继电器工作原理及选型
BYRD推出入门级入耳式耳塞系列 售价约合人民币998元
一文看懂AI制药的作用
边缘计算的应用未来可在整个人工智能领域中流行起来
泰克示波器DPO73304SX的应用
简述javascript定时器工作原理
浅析视频监控存储设备应用特点
电流对白光LED光学特性的影响
U型滑触线的工作原理 U型滑触线的应用场景有哪些
单片机 I/O口使用
慧荣科技NVMe 2.0实现混合式NAND操作
用向日葵坐席远程运维,让软件外包脱离驻场苦海
三星五代V-NAND闪存芯片,使生产效率提高了30%以上
法拉电容和电解电容介绍及应用
一文解析鸿蒙LiteOS和LINUX比较
高通推出针对XR1平台的VR头显参考设计,支持一体机模式和分体模式
电机负载计算方法 运转功率及加速功率计算
vivo X Fold+亮相发布 搭载骁龙8+和全新折叠
LED光通量
爱立信预测今年中国有超千万5G用户