Maison >interface Web >js tutoriel >Introduction détaillée au code de setTimeout en JavaScript

Introduction détaillée au code de setTimeout en JavaScript

黄舟
黄舟original
2017-03-04 15:33:431429parcourir

1. Single-threading de setTimeout

Depuis longtemps, tout le monde dit que Javascript est monothread. Le navigateur n'a qu'un seul thread qui exécute JavaScript à la fois. programme.

Cependant, je ne sais pas si vous avez des doutes : le setTimeout (similaire à setInterval et Ajax) que nous utilisons dans le processus de programmation n'est-il pas exécuté de manière asynchrone ? ! !

Par exemple :

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            console.log("a");
            //利用setTimeout延迟执行匿名函数
            setTimeout(function(){
                console.log("b");
            },100);
            console.log("c");
        </script>
    </body>
</html>

Exécutez le code, ouvrez le débogueur Chrome et obtenez les résultats suivants

Ce résultat est facile à comprendre, car le contenu de mon setTimeout est exécuté après 100 ms. Bien sûr, a est sorti en premier, puis c, et enfin b dans setTimeout. sortie après 100 ms.

Hé, Javascript n'est-il pas monothread ? Peut-il être multithread ? ! !

En fait, non. setTimeout ne rompt pas le mécanisme monothread de JavaScript, il est en fait toujours monothread.

Pourquoi dites-vous cela ? Alors vous devez comprendre ce qu'est setTimeout.

Veuillez regarder le code ci-dessous et devinez le résultat :

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            var date = new Date();
            //打印才进入时的时间
            console.log(&#39;first time: &#39; + date.getTime());
            //一秒后打印setTimeout里匿名函数的时间
            setTimeout(function(){
                var date1 = new Date();
                console.log(&#39;second time: &#39; + date1.getTime() );
                console.log( date1.getTime() - date.getTime() );
            },1000);
            //重复操作
            for(var i=0; i < 10000 ; i++){
                console.log(1);
            }
        </script>
    </body>
</html>

Regardez le code ci-dessus et devinez quel est le résultat de sortie ? 1000 millisecondes ?

On ouvre le débogueur Chrome, voir l'image ci-dessous

Nani, pourquoi ce n'est pas 1000 millisecondes ? ! ! !

Regardons à nouveau le code suivant :

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            //一秒后执行setTimeout里的匿名函数,alert下
            setTimeout(function(){
                alert("monkey");
            },1000);
            while(true){};
        </script>
    </body>
</html>

Après avoir exécuté le code !

Putain de merde, pourquoi je continue à actualiser, le navigateur est bloqué et il n'y a pas d'alerte ! !

Logiquement parlant, même si mon while tourne à l'infini, je dois alerter au bout d'1 seconde.

Tous les problèmes ont la même raison, JavaScript est monothread .

N'oubliez pas que JavaScript est monothread et que setTimeout n'implémente pas le multithreading. La vérité derrière cela est la suivante :

Le moteur JavaScript s'exécute dans un seul thread, peu importe. où se trouve le navigateur. Il n'y a qu'un seul thread exécutant le programme JavaScript à tout moment.

Le noyau du navigateur est multi-thread. Ils coopèrent entre eux sous le contrôle du noyau pour maintenir la synchronisation. Un navigateur implémente au moins trois threads résidents : JavaScript. fil de moteur, fil de rendu GUI, fil de déclenchement d'événements du navigateur .

*Le moteur JavaScript est basé sur une exécution monothread pilotée par des événements. Le moteur JavaScript attend l'arrivée des tâches dans la file d'attente des tâches et. puis les traite, quel que soit le navigateur. Il n'y a qu'un seul thread JavaScript qui exécute le programme JavaScript à tout moment.

*Le fil de rendu de l'interface graphique est responsable du rendu de l'interface du navigateur lorsque l'interface doit être redessinée (Repaint) ou qu'une redistribution est effectuée. causé en raison d'une opération (Reflow), le thread s'exécutera. Cependant, il convient de noter que le thread de rendu de l'interface graphique et le moteur JavaScript s'excluent mutuellement. Lorsque le moteur JavaScript est exécuté, le thread de l'interface graphique sera suspendu et les mises à jour de l'interface graphique seront enregistrées dans une file d'attente et seront exécutées immédiatement lorsque le JavaScript. le moteur est au ralenti.

*Fil de déclenchement d'événement , lorsqu'un événement est déclenché, le fil ajoutera l'événement à la fin de la file d'attente en attente et attendra Traitement du moteur JavaScript. Ces événements peuvent provenir du bloc de code actuellement exécuté par le moteur JavaScript tel que setTimeout, ou d'autres threads du noyau du navigateur tels que des clics de souris, des requêtes asynchrones Ajax, etc. Cependant, en raison de la relation monothread de JavaScript, tous ces événements doivent être mis en file d'attente pour être traités par le moteur JavaScript (le code asynchrone ne sera exécuté que lorsqu'aucun code synchrone n'est exécuté dans le thread).

ainsi, grâce à l'explication ci-dessus, tous les problèmes ci-dessus peuvent être facilement résolus.

2. Le temps de retard de setTimeout est de 0

Lorsque le temps de retard de setTimeout est de 0, pensez à la façon dont il sera exécuté ?

Par exemple, le code suivant :

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            console.log(&#39;a&#39;);
            setTimeout(function(){
                console.log(&#39;b&#39;);
            },0);
            console.log(&#39;c&#39;);
            console.log(&#39;d&#39;);
        </script>
    </body>
</html>

Le résultat de l'exécution du code est le suivant :

Supposons que vous sachiez déjà comment fonctionne l'opération Javascript monothread. Ensuite, vous vous posez peut-être cette question : le délai setTimeout est de 0 et n'a pas été ajouté à la fin de la file d'attente de traitement. Comment peut-il être exécuté en retard ? Ne devrait-il pas être fait immédiatement ?

Je crois comprendre que même si le temps setTimeout est 0, il est toujours setTimeout et le principe reste inchangé. Il sera donc ajouté à la fin de la file d'attente et exécuté après 0 seconde.

De plus, Après avoir recherché des informations, j'ai découvert que setTimeout a un temps d'exécution minimum. Lorsque le temps spécifié est inférieur à ce temps, le navigateur utilisera le temps minimum autorisé comme heure. intervalle pour setTimeout C'est-à-dire que même si nous définissons les millisecondes de setTimeout à 0, le programme appelé ne démarre pas immédiatement.

Quel est l'intervalle de temps minimum ?

这和浏览器及操作系统有关。在John Resig的《Javascript忍者的秘密》一书中提到–Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.(在苹果机上的最小时间间隔是10毫秒,在Windows系统上的最小时间间隔大约是15毫秒),另外,MDC中关于setTimeout的介绍中也提到,Firefox中定义的最小时间间隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定义的最小时间间隔是4毫秒。

说了这么多,setTimeout的延迟时间为0,看来没什么意义嘛,都是放在队列后执行嘛。

非也,天生我材必有用,就看你怎么用咯。抛砖迎玉下。

1、可以用setTimeout的延迟时间为0,模拟动画效果哦。

详情请见下代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <p id="container" style="width:100px;height:100px;border:1px solid black;"></p>
        <p id="btn" style="width:40px;height:40px;line-height:40px;margin-top:20px;background:pink;">click</p>
        <script>
            window.onload = function(){
                var con = document.getElementById(&#39;container&#39;);
                var btn = document.getElementById(&#39;btn&#39;); 
                //Params: i 为起始高度,num为预期高度
                function render(i, num) {
                    i++; 
                    con.style.height = i + &#39;px&#39;;
                    //亮点在此
                    if(i < num){
                        setTimeout(function() {
                            render(i, num);
                        },0);
                    }
                    else {
                        con = null;
                        btn = null;
                    }
                };
                btn.onclick = function(){
                    render(100, 200);
                };
            };
        </script>
    </body>
</html>

由于是动画,所以想看其效果,还请各位看官运行下代码哦。

代码第19行中,利用setTimeout,在每一次render执行完成(给高度递增1)后,由于Javascript是单线程,且setTimeout里的匿名函数会在render执行完成后,再执行render。所以可以实现动画效果。

2、可以用setTimeout的延迟时间为0,实现捕获事件哦。

当我们点击子元素时,我们可以利用setTimeout的特性,来模拟捕获事件。

请见如下代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style>    
            #parent {
                width:100px;
                height:100px;
                border:1px solid black;
            }   
            #child {
                width:50px;
                height:50px;
                background:pink;
            }
        </style>
    </head>
    <body>
        <p id="parent">
            <p id="child"></p>
        </p>
        <script>
            //点击子元素,实现子元素的事件在父元素触发后触发
            window.onload = function(){
                var parent = document.getElementById(&#39;parent&#39;); 
                var child = document.getElementById(&#39;child&#39;);
                parent.onclick = function(){
                    console.log(&#39;parent&#39;);
                }
                child.onclick = function(){
                    //利用setTimeout,冒泡结束后,最后输出child
                    setTimeout(function(){
                        console.log(&#39;child&#39;);   
                    },0);
                }
                parent = null;
                child = null;   
            }
        </script>
    </body>
</html>

 执行代码,点击粉红色方块,输出结果:


三、setTimeout那些事儿之this

说到this,对于它的理解就是:this是指向函数执行时的当前对象,倘若没有明确的当前对象,它就是指向window的。

好了,那么我们来看看下面这段代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
           var name = &#39;!!&#39;;
           var obj = {
               name:&#39;monkey&#39;,
               print:function(){
                   console.log(this.name);
               },
               test:function(){
                   //this.print
                   setTimeout(this.print,1000);
               }
           }
           obj.test();
        </script>
    </body>
</html>

通过chrome调试器,查看输出结果:

咦,它怎么输出的是”!!”呢?不应该是obj里的”monkey”吗?!!

这是因为setTimeout中所执行函数中的this,永远指向window。

不对吧,那上面代码中的setTimeout(this.print,1000)里的this.print怎么指向的是obj呢?!!

注意哦,我这里说的是“延迟执行函数中的this”,而不是setTimeout调用环境下的this。

什么意思?

setTimeout(this.print,1000),这里的this.print中的this就是调用环境下的;

而this.print=function(){console.log(this.name);},这个匿名函数就是setTimeout延迟执行函数,其中的this.name也就是延迟执行函数中的this啦。

嘿嘿,这下明白了吧。

var age = 24;
function Fn(){
    this.age = 18;
    setTimeout(function(){
        //this代表window
        console.log(this);
        //输出24,而不是Fn的18
        console.log(this.age);
    },1000);
}
new Fn();

咦,那有个疑问,比如我想在setTimeout延迟执行函数中的this指向调用的函数呢,而不是window?!!我们该怎么办呢。

常用的方法就是利用that。

that?

对,that。利用闭包的知识,让that保证你传进去的this,是你想要的。

详情见下:

var age = 24;
function Fn(){
    //that在此
    var that = this;
    this.age = 18;
    setTimeout(function(){
        console.log(that);
        console.log(that.age);
    },1000);
}
new Fn();

还有一种方法就是,利用bind。

如下:

var age = 24;
function Fn(){
    this.age = 18;
    //bind传入this
    setTimeout(function(){
        console.log(this);
        console.log(this.age);
    }.bind(this),1000);
}
new Fn();

 以上就是JavaScript 中 setTimeout的代码详情介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn