zoukankan      html  css  js  c++  java
  • JavaScript中的setTimeout和setInterval

    上一篇博文<浏览器中Javascript单线程分析>中描述了浏览器中Javascript单线程的原理。

    在此基础上,这篇文章将主要介绍setTimeout/setInterval是如何模拟异步的,且二者之间又有何区别。

    首先我们来分析它们如何模拟异步。

    可以根据上篇博文了解到JS引擎内部维护一个队列,用来存放各种回调函数,其中也包括setTimeout/setInterval回调。

    下面用代码结合图形的方式来描述异步是如何产生的。

    先看例1:

    <html>
    <body>
    <script>
        setTimeout(function(){
            console.log(1);
        },0);
        console.log(2);
    </script>
    </body>
    </html>

    输出结果为:2 1

    其队列可描述为:

    其中code snippet指<script>里的逻辑代码段;setTimeout callback指定时器回调。

    虽然setTimeout设为0ms,但当前有代码段在执行,只能将回调置于队列后面。

    再看例2:

    <html>
    <body>
    <script>
        setInterval(function(){
            console.log(1);
        },0);
        console.log(2);
    </script>
    </body>
    </html>

    输出结果为:2 1 1 1 1 ...

    其队列可描述为:

    至于为什么不是 1 1 1 ... 2,原理和setTimeout(..,0)是一致的。

    从上看到通过回调队列实现了延时函数setTimeout/setInterval。

    那么问题来了,这两个函数的延时是否精确,也就是说是否按照设置的延时时间执行?

    接着看例3 setTimout:

    <html>
    <body>
    <script>
        setTimeout(function(){
            console.log(1);
        },1000);
        //耗时处理超过1s,假设约2s
    doSomething(); </script> </body> </html>

    结果:延迟输出1会在doSomething()执行后大约2s后执行。

    这是为什么,我们可以用上一篇博客的原理来解释,还是用队列图来描述:

    再来看例4 setInterval:

    <html>
    <body>
    <script>
        setInterval(function(){
            console.log(1);
        },10);
        //耗时处理超过1s,假设约2s
    doSomething(); </script> </body> </html>

    结果:延迟输出第一个1会在doSomething()执行后大约2s后执行,紧接着马上输出第二个1,时间间隔并没有10ms。

    之所以间隔没有10ms,是因为定时回调在插入到队列时发现预期时间点被doSomething占用,只能后延插入的回调,

    这样会导致第二次定时回调会直接放在第一个定时回调后,所以在执行时没有间隔。

    队列图如下:

    从上面的结果可以回答我们之前提出的疑问,setTimout/setInterval不能保证一定在延时达到时执行回调。

    既然不能保证延时时间,那产生上面的结果的原因又是什么呢?且两个函数在延时上有什么区别呢?

    让我们来看例5:

    <html>
    <body>
    <script>
    setTimeout(function(){
    //耗时函数,假设为2s
    doSomething();
    setTimout(arguments.callee,1000);
    },1000); setInterval(function(){
    //耗时函数,假设为2s
    doSomething(); },1000); </script> </body> </html>

    从逻辑意义上,如果setTimeout/setInterval延时没有偏移,那么两段断码意义一致。

    但从上面实验结果表明二者均存在延时偏移,那么偏移是怎么产生的,且二者偏移有何不同?

    还是用两个队列来描述着两段代码的执行结果。

    setTimeout:

    setInterval:

    从上我们便可以清楚地看到,setTimeout每次递归会等待上一次回调函数执行完成,而回调中存在耗时函数,所以会超过1s才能执行下一次回调;

    而setInterval不会等待回调执行完成,而是将回调每隔1s插入到回调队列中,如果该时间点存在其他回调,则该时间点的回调往后移动,后面的定时回调时间点不受影响,

    因为回调中存在耗时函数,所以定义器不能刚好将回调插入预设的时间点上,而只能插入到耗时函数后,这会导致后面的定时回调函数堆积,执行时时间间隔不会超过1s。

    图中层叠关系代表定时回调函数堆积(有序)。

    总的来说,也就是setTimeout延时间隔可能大于设置的时间,而setInterval延时间隔可能小于设置的时间。

    关于浏览器单线程原理和setTimout/setInterval原理就介绍到这。

  • 相关阅读:
    五大存储模型关系模型、键值存储、文档存储、列式存储、图形数据库
    UML语言中类之间关系
    hadoop的yarn资源队列
    Scala中下划线的7种用法
    《机器学习》(周志华)西瓜书读书笔记(完结)
    Pytorch DistributedDataParallel简明使用指南
    本地查看SSH远程连接服务器上的TensorBoard
    python max()用法
    实现go并发的三种方式
    docker分阶段构造nginx镜像
  • 原文地址:https://www.cnblogs.com/tesky0125/p/4619517.html
Copyright © 2011-2022 走看看