0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JavaScript定时器与执行机制介绍

jf_78858299 来源:前端大全 作者:前端大全 2023-04-21 14:31 次阅读

从JS执行机制说起

浏览器(或者说JS引擎)执行JS的机制是基于事件循环。

由于JS是单线程,所以同一时间只能执行一个任务,其他任务就得排队,后续任务必须等到前一个任务结束才能开始执行。

为了避免因为某些长时间任务造成的无意义等待,JS引入了异步的概念,用另一个线程来管理异步任务。

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

JS定时器

JS的定时器目前有三个:setTimeout、setInterval和setImmediate。

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

JS定时器非常实用,做动画的肯定都用到过,也是最常用的异步模型之一。

有时候一些奇奇怪怪的问题,加一个setTimeout(fn, 0)(以下简写setTimeout(0))就解决了。不过,如果对定时器本身不熟悉,也会产生一些奇奇怪怪的问题。

setTimeout

setTimeout(fn, x)表示延迟x毫秒之后执行fn。

使用的时候千万不要太相信预期,延迟的时间严格来说总是大于x毫秒的,至于大多少就要看当时JS的执行情况了。

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

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

setTimeout注册的函数fn会交给浏览器的定时器模块来管理,延迟时间到了就将fn加入主进程执行队列,如果队列前面还有没有执行完的代码,则又需要花一点时间等待才能执行到fn,所以实际的延迟时间会比设置的长。如在fn之前正好有一个超级大循环,那延迟时间就不是一丁点了。

(function testSetTimeout() {

const label = 'setTimeout';

console.time(label);

setTimeout(() => {

    console.timeEnd(label);

}, 10);

for(let i = 0; i < 100000000; i++) {}

})();

结果是:setTimeout: 335.187ms,远远不止10ms。

setInterval

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

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

(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

可见,虽然每次fn执行时间都很长,但下一次并不是等上一次执行完了再过100ms才开始执行的,实际上早就已经等在队列里了。

另外可以看出,当setInterval的回调函数执行时间超过了延迟时间,已经完全看不出有时间间隔了。

如果setTimeout和setInterval都在延迟100ms之后执行,那么谁先注册谁就先执行回调函数。

setImmediate

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

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

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

(function testSetImmediate() {

const label = 'setImmediate';

console.time(label);


setImmediate(() => {

    console.timeEnd(label);

});

})();

Edge输出:setImmediate: 0.555 毫秒

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

其他常用异步模型

requestAnimationFrame

requestAnimationFrame并不是定时器,但和setTimeout很相似,在没有requestAnimationFrame的浏览器一般都是用setTimeout模拟

requestAnimationFrame跟屏幕刷新同步,大多数屏幕的刷新频率都是60Hz,对应的requestAnimationFrame大概每隔16.7ms触发一次,如果屏幕刷新频率更高,requestAnimationFrame也会更快触发。基于这点,在支持requestAnimationFrame的浏览器还使用setTimeout做动画显然是不明智的。

在不支持requestAnimationFrame的浏览器,如果使用setTimeout/setInterval来做动画,最佳延迟时间也是16.7ms。 如果太小,很可能连续两次或者多次修改dom才一次屏幕刷新,这样就会丢帧,动画就会卡;如果太大,显而易见也会有卡顿的感觉。

有趣的是,第一次触发requestAnimationFrame的时机在不同浏览器也存在差异,Edge中,大概16.7ms之后触发,而Chrome则立即触发,跟setImmediate差不多。按理说Edge的实现似乎更符合常理。

Edge输出:requestAnimationFrame: 16.66 毫秒

Chrome输出:requestAnimationFrame: 0.698ms

但相邻两次requestAnimationFrame的时间间隔大概都是16.7ms,这一点是一致的。当然也不是绝对的,如果页面本身性能就比较低,相隔的时间可能会变大,这就意味着页面达不到60fps。

Promise

Promise是很常用的一种异步模型,如果我们想让代码在下一个事件循环执行,可以选择使用setTimeout(0)、setImmediate、requestAnimationFrame(Chrome)和Promise。

而且Promise的延迟比setImmediate更低,意味着Promise比setImmediate先执行。

function testSetImmediate() {

const label = 'setImmediate';

console.time(label);


setImmediate(() => {

    console.timeEnd(label);

});

}

function testPromise() {

const label = 'Promise';

console.time(label);

new Promise((resolve, reject) => {

    resolve();

}).then(() => {

    console.timeEnd(label);

});

}

testSetImmediate();

testPromise();

Edge输出:Promise: 0.33 毫秒 setImmediate: 1.66 毫秒

尽管setImmediate的回调函数比Promise先注册,但还是Promise先执行。

可以肯定的是,在各JS环境中,Promise都是最先执行的,setTimeout(0)、setImmediate和requestAnimationFrame顺序不确定。

process.nextTick

process.nextTick是Nodejs的API,比Promise更早执行。

事实上,process.nextTick是不会进入异步队列的,而是直接在主线程队列尾强插一个任务,虽然不会阻塞主线程,但是会阻塞异步任务的执行,如果有嵌套的process.nextTick,那异步任务就永远没机会被执行到了。

使用的时候要格外小心,除非你的代码明确要在本次事件循环结束之前执行,否则使用setImmediate或者Promise更保险。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 定时器
    +关注

    关注

    23

    文章

    3241

    浏览量

    114489
  • JS
    JS
    +关注

    关注

    0

    文章

    78

    浏览量

    18076
  • 循环
    +关注

    关注

    0

    文章

    92

    浏览量

    15957
  • 单线程
    +关注

    关注

    0

    文章

    17

    浏览量

    1769
收藏 人收藏

    评论

    相关推荐

    RT-Thread定时器工作机制以及定时器的管理方式

    当不再需要动态定时器时,可以将其删除,执行如下函数之后系统会把这个定时器从 rt_timer_list 链表中删除,然后释放相应的定时器控制块占有的内存:rt_err_t rt_tim
    的头像 发表于 02-15 10:36 1.2w次阅读
    RT-Thread<b class='flag-5'>定时器</b>工作<b class='flag-5'>机制</b>以及<b class='flag-5'>定时器</b>的管理方式

    请问软件定时器执行的回调函数是否有优先级机制

    最近小弟在学习UCOSIII系统,到了软件定时器优先级这一部分,有一个问题一直卡在心里。软件定时器到达后执行的回调函数有没有优先级这种机制?是不是说,软件
    发表于 05-27 05:51

    555定时器

    555定时器555定时器555定时器555定时器555定时器555定时器555
    发表于 11-10 17:25 52次下载

    定时器介绍

    同时用两个定时器控制蜂鸣器发声, 定时器0控制频率,定时器1控制同个 频率持续的时间,间隔2s依次输出 1,10,50100,200400800
    发表于 02-23 15:56 20次下载

    STM32定时器-基本定时器

    目录定时器分类基本定时器功能框图讲解基本定时器功能时钟源计数时钟计数自动重装载寄存
    发表于 11-23 18:21 31次下载
    STM32<b class='flag-5'>定时器</b>-基本<b class='flag-5'>定时器</b>

    STM32定时器US延时

    定时器介绍的SysTick定时器,该定时器位于Cortex-M3内核中。外设定时器由芯片半导体厂商设计,如STM32系列,包含常规
    发表于 12-05 15:36 25次下载
    STM32<b class='flag-5'>定时器</b>US延时

    软件定时器简介及程序配置

      软件定时器就是允许函数设置一定的等待时间,然后执行定时器执行的函数被称为定时器的回调函数。定时器
    的头像 发表于 12-06 16:10 3846次阅读
    软件<b class='flag-5'>定时器</b>简介及程序配置

    STM32定时器的分类及中断原理

    本文主要介绍常规定时器中的TIM3,实现定时器中断的功能。STM32定时器的分类在其中一篇文章中已经介绍过,本文主要内容主要
    的头像 发表于 01-12 17:29 7099次阅读
    STM32<b class='flag-5'>定时器</b>的分类及中断原理

    555定时器及应用介绍

    555定时器及应用介绍
    的头像 发表于 04-12 09:14 1573次阅读

    STM32定时器介绍

    本文将介绍STM32定时器的一些基本概念性的东西,方便后面内容的理解学习。
    的头像 发表于 04-21 11:33 2985次阅读
    STM32<b class='flag-5'>定时器</b><b class='flag-5'>介绍</b>

    简述javascript定时器工作原理

    说到 javascript 中的定时器,我们肯定会想到 setTimeout() 和 setInterval() 这两个函数。本文将从事件循环(Event Loop) 的角度来分析两者的工作原理和区别。
    的头像 发表于 04-21 14:32 778次阅读
    简述<b class='flag-5'>javascript</b><b class='flag-5'>定时器</b>工作原理

    定时器作用及实现定时器数据结构选取介绍1

    定时器在各种场景都需要用到,比如游戏的Buff实现,Redis中的过期任务,Linux中的定时任务等等。顾名思义,定时器的主要用途是执行定时
    的头像 发表于 04-21 15:20 1153次阅读
    <b class='flag-5'>定时器</b>作用及实现<b class='flag-5'>定时器</b>数据结构选取<b class='flag-5'>介绍</b>1

    定时器作用及实现定时器数据结构选取介绍2

    定时器在各种场景都需要用到,比如游戏的Buff实现,Redis中的过期任务,Linux中的定时任务等等。顾名思义,定时器的主要用途是执行定时
    的头像 发表于 04-21 15:20 1157次阅读
    <b class='flag-5'>定时器</b>作用及实现<b class='flag-5'>定时器</b>数据结构选取<b class='flag-5'>介绍</b>2

    高级定时器的功能介绍

      本文将介绍高级定时器的功能。
    的头像 发表于 05-01 09:01 3156次阅读
    高级<b class='flag-5'>定时器</b>的功能<b class='flag-5'>介绍</b>

    FreeRTOS的定时器设计实现

    定时器用于根据系统时启动特定的函数,执行相应的任务。FreeRTOS的定时器可以配置启动一次或者间隔一定时执行
    的头像 发表于 07-25 15:28 1918次阅读
    FreeRTOS的<b class='flag-5'>定时器</b>设计实现