前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript设置定时器、取消定时器及执行机制解析

JavaScript设置定时器、取消定时器及执行机制解析

作者头像
德顺
发布2019-11-12 23:12:58
4.5K0
发布2019-11-12 23:12:58
举报
文章被收录于专栏:前端资源前端资源

今天整理了一下 JavaScript 定时器,顺便了解了一下 JavaScript 的运行机制,现在记录一下。

JavaScript 执行机制

浏览器( JavaScript 引擎)执行 JavaScript 的机制是基于事件循环的。由于 JavaScript 是单线程,同一时间只能执行一个任务。为了避免某些长时间任务造成无意义等待,JavaScript 引入了异步概念。

同步任务直接在主线程队列中顺序执行,而异步任务会进入另一个任务队列,不会阻塞主线程。等到主线程队列空了(执行完了)的时候,就会去异步队列查询是否有可执行的异步任务了(异步任务通常进入异步队列之后还要等一些条件才能执行,如 ajax 请求、文件读写),如果某个异步任务可以执行了便加入主线程队列,以此循环。

JavaScript 定时器

定时器也是一种异步任务,通常浏览器都有一个独立的定时器模块,定时器的延迟时间就由定时器模块来管理,当某个定时器到了可执行状态,就会被加入主线程队列。

了解了上面的执行机制,我们不难理解 JavaScript 定时器不是绝对精准的,延迟的时间严格来说总是大于我们设定的时间的,至于大多少就要看当时 JavaScript 的执行情况了。

另外,多个定时器如不及时清除clearTimeout()),会造成干扰,使延迟时间更加捉摸不透。所以,不管定时器有没有执行完,要及时清除不需要的定时器。

下面来介绍几个常用的 JavaScript 定时器:

setTimeout()

设置一个定时器,在定时器到期后执行一次函数或代码段:setTimeout(fn, x) 表示延迟 x 毫秒之后执行 fn

代码语言:javascript
复制
var timeoutId = setTimeout(code, delay, param1, param2, ...)
var timeoutId = setTimeout(func, delay, param1, param2, ...)

timeoutId 定时器ID

func 延迟后执行的函数

code 延迟后执行的代码字符串,不推荐使用原理类似eval()

delay 延迟的时间(单位:毫秒),默认值为0

param1,param2 向延迟函数传递而外的参数,IE9以上支持

HTML5 规范规定最小延迟时间不能小于 4ms ,即 x 如果小于 4 ,会被当做 4 来处理。 不过不同浏览器的实现不一样,比如,Chrome可以设置1ms,IE11/Edge是4ms。

另外, setTimeout() 方法不是 Ecmascript 规范定义的内容,而是属于BOM提供的功能。setTimeout() 延迟也是有上限的,如果大于 2  的 31 次方的话,会立即执行,延迟无效。

setInterval()

以固定的时间间隔重复调用一个函数或者代码段:

代码语言:javascript
复制
var intervalId = window.setInterval(func, delay , param1, param2, ...]);
var intervalId = window.setInterval(code, delay);

intervalId 重复操作的ID

func 延迟调用的函数

code 代码段

delay 延迟时间,没有默认值

setInterval 的实现机制跟 setTimeout 类似,只不过是重复执行的。

对于 setInterval(fn, 100) 容易产生一个误区:并不是上一次 fn 执行完了之后再过 100ms 才开始执行下一次 fn。 事实上,setInterval 并不管上一次 fn 的执行结果,而是每隔 100ms 就将 fn 放入主线程队列,而两次 fn 之间具体间隔多久就不一定了,跟 setTimeout 实际延迟时间类似,和 JavaScript 执行情况有关。

代码语言:javascript
复制
(function testSetInterval() {
    let i = 0;
    const start = Date.now();
    const timer = setInterval(() => {
        i += 1;
        i === 5 && clearInterval(timer);
        console.log(`第${i}次开始`, Date.now() - start);
        for(let i = 0; i < 100000000; i++) {}
        console.log(`第${i}次结束`, Date.now() - start);
    }, 100);
})();
/*输出
第1次开始 100
第1次结束 1089
第2次开始 1091
第2次结束 1396
第3次开始 1396
第3次结束 1701
第4次开始 1701
第4次结束 2004
第5次开始 2004
第5次结束 2307 */
setImmediate()

浏览器完全结束当前运行的操作之后立即执行指定的函数(仅 IE10 和 Node 0.10+ 中有实现),类似 setTimeout(func, 0)

代码语言:javascript
复制
var immediateId = setImmediate(func , param1, param2, ...]);
var immediateId = setImmediate(func);

immediateId 定时器ID

func 回调

这算一个比较新的定时器,目前IE11/Edge支持、Nodejs支持,Chrome不支持,其他浏览器未测试。

从API名字来看很容易联想到setTimeout(0),不过setImmediate应该算是setTimeout(0)的替代版。

在IE11/Edge中,setImmediate延迟可以在1ms以内,而setTimeout有最低4ms的延迟,所以setImmediate比setTimeout(0)更早执行回调函数。不过在Nodejs中,两者谁先执行都有可能,原因是Nodejs的事件循环和浏览器的略有差异。

代码语言:javascript
复制
(function testSetImmediate() {
    const label = 'setImmediate';
    console.time(label);
 
    setImmediate(() => {
        console.timeEnd(label);
    });
})();
/* Edge输出:setImmediate: 0.555 毫秒 */

很明显,setImmediate设计来是为保证让代码在下一次事件循环执行,以前setTimeout(0)这种不可靠的方式可以丢掉了。

requestAnimationFrame()

专门为实现高性能的帧动画而设计的API,但是不能指定延迟时间,而是根据浏览器的刷新频率(帧)而定。

代码语言:javascript
复制
var requestId = window.requestAnimationFrame(func);

func 回调

上面简单的介绍了四种 JavaScript 的定时器,而本文将会主要介绍比较常用的两种:setTimeout()setInterval()

clearTimeout() 取消定时器

clearInterval() 方法可取消由 setInterval() 函数设定的定时执行操作。

clearInterval() 方法的参数必须是由 setInterval() 返回的 ID 值。

代码语言:javascript
复制
myVar = setInterval(func, 200); // 设置一个定时器
clearInterval(myVar); // 取消这个定时器

myVar 调用 setInterval() 函数时所获得的返回值,使用该返回标识符作为参数,可以取消该 setInterval() 所设定的定时执行操作。

举例:

基本用法:

代码语言:javascript
复制
// 下面代码执行之后会输出什么?
var intervalId, timeoutId;

timeoutId = setTimeout(function () {
    console.log(1);
}, 300);

setTimeout(function () {
    clearTimeout(timeoutId);
    console.log(2);
}, 100);

setTimeout('console.log("5")', 400);

intervalId = setInterval(function () {
    console.log(4);
    clearInterval(intervalId);
}, 200);

// 分别输出: 2、4、5

setInterval 和 setTimeout的区别:

代码语言:javascript
复制
// 执行在面的代码块会输出什么?
setTimeout(function () {
    console.log('timeout');
}, 1000);

setInterval(function () {
    console.log('interval')
}, 1000);

// 输出一次 timeout,每隔1S输出一次 interval

/*--------------------------------*/

// 通过setTimeout模拟setInterval 和 setInterval有啥区别么?
var callback = function () {
    if (times++ > max) {
        clearTimeout(timeoutId);
        clearInterval(intervalId);
    }

    console.log('start', Date.now() - start);
    for (var i = 0; i < 990000000; i++) {}
    console.log('end', Date.now() - start);
},
delay = 100,
times = 0,
max = 5,
start = Date.now(),
intervalId, timeoutId;

function imitateInterval(fn, delay) {
    timeoutId = setTimeout(function () {
        fn();

        if (times <= max) {
            imitateInterval(fn ,delay);
        }
    }, delay);
}

imitateInterval(callback, delay);
intervalId = setInterval(callback, delay);

如果是setTimeoutsetInterval的话,它俩仅仅在执行次数上有区别,setTimeout一次、setIntervaln次。

而通过setTimeout模拟的setIntervalsetInterval的区别则在于:setTimeout只有在回调完成之后才会去调用下一次定时器,而setInterval则不管回调函数的执行情况,当到达规定时间就会在事件队列中插入一个执行回调的事件,所以在选择定时器的方式时需要考虑setInterval的这种特性是否会对你的业务代码有什么影响?

setTimeout(func, 0) 和 setImmediate(func)谁更快?(仅仅是好奇,才写的这段测试)

代码语言:javascript
复制
console.time('immediate');
console.time('timeout');

setImmediate(() => {
    console.timeEnd('immediate');
});

setTimeout(() => {
    console.timeEnd('timeout');
}, 0);
}, 0);

Node.JS v6.7.0中测试发现setTimeout更早执行。

声明:本文由w3h5原创,转载请注明出处:《JavaScript设置定时器、取消定时器及执行机制解析》 https://cloud.tencent.com/developer/article/1537803

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JavaScript 执行机制
  • JavaScript 定时器
    • setTimeout()
      • setInterval()
        • setImmediate()
          • requestAnimationFrame()
          • clearTimeout() 取消定时器
          • 举例:
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档