Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Einführung in den Code von setTimeout in JavaScript

Detaillierte Einführung in den Code von setTimeout in JavaScript

黄舟
黄舟Original
2017-03-04 15:33:431390Durchsuche

1. Single-Threading von setTimeout

Seit langem sagt jeder, dass im Browser immer nur ein Thread läuft Programm.

Ich weiß jedoch nicht, ob Sie irgendwelche Zweifel haben – wird das setTimeout (ähnlich wie setInterval und Ajax), das wir im Programmierprozess verwenden, nicht asynchron ausgeführt? ! !

Zum Beispiel:

<!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>

Führen Sie den Code aus, öffnen Sie den Chrome-Debugger und erhalten Sie die folgenden Ergebnisse

Dieses Ergebnis ist leicht zu verstehen, da der Inhalt in meinem setTimeout nach 100 ms ausgeführt wird. Natürlich wird zuerst a ausgegeben, dann c und dann b in setTimeout Ausgabe nach 100ms.

Hey, ist Javascript nicht Single-Threaded? Kann es Multi-Threaded sein? ! !

Eigentlich nein. setTimeout unterbricht nicht den Single-Thread-Mechanismus von JavaScript, sondern ist tatsächlich Single-Threaded.

Warum sagen Sie das? Dann müssen Sie verstehen, was setTimeout ist.

Bitte schauen Sie sich den Code unten an und erraten Sie das Ergebnis:

<!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>

Schauen Sie sich den Code oben an und erraten Sie, was das Ausgabeergebnis ist? 1000 Millisekunden?

Wir öffnen den Chrome-Debugger, siehe Bild unten

Nani, warum sind es nicht 1000 Millisekunden? ! ! !

Schauen wir uns noch einmal den folgenden Code an:

<!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>

Nach dem Ausführen des Codes!

Heilige Scheiße, warum aktualisiere ich ständig, der Browser bleibt hängen und es gibt keine Warnung! !

Logischerweise muss ich nach 1 Sekunde immer noch eine Warnung auslösen, selbst wenn ich eine Endlosschleife mache.

Alle Probleme haben den gleichen Grund, JavaScript ist Single-Threaded .

Denken Sie daran, dass JavaScript Single-Threading ist und setTimeout kein Multi-Threading implementiert. Die Wahrheit dahinter ist folgende:

Die JavaScript-Engine läuft in einem einzigen Thread, egal wo sich der Browser befindet. Es gibt immer nur einen Thread, der das JavaScript-Programm ausführt.

Der Kernel des Browsers ist multithreaded. Sie arbeiten unter der Kontrolle des Kernels zusammen, um die Synchronisierung aufrechtzuerhalten: JavaScript Engine-Thread, GUI-Rendering-Thread, Browser-Ereignis-Trigger-Thread .

*Die JavaScript-Engine basiert auf der ereignisgesteuerten Single-Thread-Ausführung. Die JavaScript-Engine hat auf das Eintreffen von Aufgaben in der Aufgabenwarteschlange gewartet Verarbeitet sie dann unabhängig vom Browser. Es gibt immer nur einen JavaScript-Thread, der das JavaScript-Programm ausführt.

*GUI-Rendering-Thread ist für das Rendern der Browseroberfläche verantwortlich (Repaint) oder ein Reflow Aufgrund eines Vorgangs (Reflow) wird der Thread ausgeführt. Es ist jedoch zu beachten, dass sich der GUI-Rendering-Thread und die JavaScript-Engine gegenseitig ausschließen. Wenn die JavaScript-Engine ausgeführt wird, wird der GUI-Thread angehalten und GUI-Updates werden in einer Warteschlange gespeichert und sofort ausgeführt, wenn die JavaScript-Engine ausgeführt wird Motor ist im Leerlauf.

*Ereignisauslöser-Thread : Wenn ein Ereignis ausgelöst wird, fügt der Thread das Ereignis am Ende der ausstehenden Warteschlange hinzu und wartet Verarbeitung durch die JavaScript-Engine. Diese Ereignisse können aus dem Codeblock stammen, der derzeit von der JavaScript-Engine ausgeführt wird, z. B. setTimeout, oder von anderen Threads im Browserkernel, z. B. Mausklicks, asynchronen Ajax-Anforderungen usw. Aufgrund der Single-Thread-Beziehung von JavaScript jedoch alle Diese Ereignisse müssen zur Verarbeitung durch die JavaScript-Engine in die Warteschlange gestellt werden (asynchroner Code wird nur ausgeführt, wenn im Thread kein synchroner Code ausgeführt wird).

durch die obige Erklärung können alle oben genannten Probleme leicht gelöst werden.

2. Die Verzögerungszeit von setTimeout ist 0

Wenn die Verzögerungszeit von setTimeout 0 ist, überlegen Sie, wie es ausgeführt wird?

Zum Beispiel der folgende Code:

<!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>

Das Ergebnis der Ausführung des Codes ist wie folgt:

Gehen Sie davon aus, dass Sie bereits wissen, wie JavaScript-Single-Threading funktioniert. Dann haben Sie möglicherweise diese Frage: Die setTimeout-Zeit ist 0 und wurde nicht zum Ende der Verarbeitungswarteschlange hinzugefügt. Wie kann sie zu spät ausgeführt werden? Sollte das nicht sofort geschehen?

Mein Verständnis ist, dass selbst wenn die setTimeout-Zeit 0 ist, sie immer noch setTimeout ist und das Prinzip unverändert bleibt. Es wird also am Ende der Warteschlange hinzugefügt und nach 0 Sekunden ausgeführt.

Außerdem Nach der Suche nach Informationen habe ich festgestellt, dass setTimeout eine minimale Ausführungszeit hat. Wenn die angegebene Zeit kürzer als diese Zeit ist, verwendet der Browser die minimal zulässige Zeit als Zeit Das heißt, selbst wenn wir die Millisekunden von setTimeout auf 0 setzen, startet das aufgerufene Programm nicht sofort.

Was ist das Mindestzeitintervall?

这和浏览器及操作系统有关。在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)!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn