热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

一家之言:说说JavaScript计时器的工作原理

计时器是一个很牛X的东西,但是很多人其实只限于知道它的语法,缺乏对其原理的认识。计时器通过设定一定的时间段(毫秒)来异步的执行一段代码。因为Javascript是一个单线程语言,计时器提供了一种绕过这种语言限制来执行代码的能力。

看下面内容之前,看一小段代码,如果读者能说出代码的用意,那就没必要往下看了,因为你都懂。


setTimeout(function(){
/* Some long block of code… */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code… */
}, 10);


计时器是一个很牛X的东西,但是很多人其实只限于知道它的语法,缺乏对其原理的认识。计时器通过设定一定的时间段(毫秒)来异步的执行一段代码。因为 Javascript 是一个单线程语言,计时器提供了一种绕过这种语言限制来执行代码的能力。

今天就简单的来说下计时器的工作原理。

Javascript 提供了三个函数来构建和操作计时器


1 var id = setTimeout(fn, delay);
2 var id = setInterval(fn, delay);
3 clearInterval(id); clearTimeout(id);


具体的语法我就不多说了,可以查手册。为了了解计时器的工作原理,有一个概念必须记在心里:时间延迟不能被保证。什么意思,就是说你这样写setTimeout(fn, 500)并不代表fn肯定在500毫秒之后马上就执行,延迟很可能会更长。因为 Javascript 是单线程语言,所有的异步事件(包括计时器、鼠标事件或者一个 XMLHttpRequest 完成)仅仅当程序执行期间有缺口的时候才会执行,不是你规定了什么时候就什么时候执行,要知道程序员不是万能的,你写的东西最终还是要看浏览器脸色的。

下面的这张图片可以很好的说明问题,感谢 John Resig 大神。

从上往下看,左面的数字代表时间(毫秒),右面的文字代表了一系列异步事件的设置和触发,中间则是代码块。最上面的 Javascript 代码块可能是你在浏览器载入的时候执行的片段,大概耗时18毫秒,紧接着下面的 Mouse Click Callback 代码块可能是你一个鼠标事件触发时的回调函数,大概耗时11毫秒,依次类推。

Javascript 的单线程特性决定了每次只能执行一块,所以当第一块代码执行的时候(它一共运行了18毫秒),本身构造了两个计时器,期间可能用户还点了一下鼠标(你有过在网页一打开还没载完就在那乱点的情况吗)。按理说用户点完鼠标就应该马上执行那个回调函数,但是不行,Javascript 执行只有一条道嘛,在那18毫秒没跑完之前,其他代码块想执行就只能排队,没空间给你超车都。那两个计时器都是10毫秒的延迟,从图中可以看到,setTimeout也在那18毫秒执行结束之前触发了,没办法也排队吧。

终于,18毫秒后,天上一道神雷把前面的车直接劈成空气了,后面两个排队的可以过去了,但是还得一个一个,不能并列,那谁先过去呢?是不是两个人在那划拳?不是的,浏览器说的算,浏览器说,鼠标单击事件先过去,setTimeout只能继续等11毫秒。注意看图,在鼠标事件回调函数执行的时候,又一个计时器事件触发了(setInterval),等着,而且必须排在setTimeout的后面。

11毫秒过去了,setTimeout 终于可以过去了,注意看,setInterval 的第二次触发了,虽然它第一次都在排队呢,如果这个时候还向往常一样排队,最后是什么情况,setTimeout执行完了,就会连续执行两个setInterval,你设置的延迟没用了都。所以浏览器还是比较智能的,它在处理setInterval的时候,如果发现已经有排队的,就直接把新来的 Kill 掉。

接着看,轮到排队的 setInterval 第一次触发开始执行了,它执行的时候,第三次触发又到了,这一次没有排队了,所以浏览器没把它 Kill 掉,给丫排队的机会,所以你会发现这两次的setInterval的执行没有间隔的,如果你做一个幻灯片,遇到这种情况就要好好想想自己的代码是不是有问题了。

最后,再也没有别的因素干扰了 setInterval 了(假如用户被 MM 叫走了),setInterval 就按照你想要的步骤执行了。

讲到这里,开头的代码可以理解了吧。


setTimeout(function(){
/* Some long block of code… */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code… */
}, 10);


这两个函数看起来效果一样,其实不然,第一个代码块总会延迟10毫秒执行,虽然大多时候是大于10毫秒的。而第二个每到10毫秒就尝试执行,不管之前的触发执行了没有。

总结起来四条

• Javascript 引擎只有一个线程,它会迫使某些异步事件排队

• setTimeout 和 setInterval 在执行异步代码的时候有很大区别

• 假如一个计时器被阻止执行,它会等待知道遇到一个代码执行空隙,通常时间比预计的要长

• Intervals 可能会一个挨着一个执行,如果回调函数的执行时间大于间隔

AD:【见识多·多妻制】中国唯一一个男人可以娶5个老婆的地方

转载请注明出处大前端 » 一家之言:说说 Javascript 计时器的工作原理


推荐阅读
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
  • 本文介绍了Python版Protobuf的安装和使用方法,包括版本选择、编译配置、示例代码等内容。通过学习本教程,您将了解如何在Python中使用Protobuf进行数据序列化和反序列化操作,以及相关的注意事项和技巧。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了2019年上半年内蒙古计算机软考考试的报名通知和考试时间。考试报名时间为3月1日至3月23日,考试时间为2019年5月25日。考试分为高级、中级和初级三个级别,涵盖了多个专业资格。报名采取网上报名和网上缴费的方式进行,报考人员可登录内蒙古人事考试信息网进行报名。详细内容请点击查看。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文是关于自学Android的笔记,包括查看类的源码的方法,活动注册的必要性以及布局练习的重要性。通过学习本文,读者可以了解到在自学Android过程中的一些关键点和注意事项。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
author-avatar
liuyidii
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有