ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript の setTimeout コードの詳細な紹介

JavaScript の setTimeout コードの詳細な紹介

黄舟
黄舟オリジナル
2017-03-04 15:33:431344ブラウズ

1. setTimeout についてのシングルスレッド

長い間、ブラウザーで JavaScript プログラムを実行するスレッドが 1 つだけであっても、JavaScript はシングルスレッドであると誰もが言ってきました。

しかし、疑問があるかどうかはわかりません。プログラミング プロセスで使用する setTimeout (setInterval や Ajax に似ています) は非同期で実行されるのではありませんか? ! !

例:

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

コードを実行してChromeデバッガーを開くと、次の結果が得られます

setTimeoutの内容が実行されるため、この結果は理解しやすいです100ms 後はもちろん、100ms 後の setTimeout で最初に a を出力し、次に c を出力し、次に b を出力することです。

ねえ、JavaScript はシングルスレッドじゃないの? マルチスレッドに対応できるの? ! !

実際には、違います。 setTimeout は JavaScript のシングルスレッド メカニズムを壊すものではなく、実際には依然としてシングルスレッドです。

なぜこんなことを言うのですか? それでは、setTimeout が何であるかを理解する必要があります。

以下のコードを見て、結果を推測してください:

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

上記のコードを見て、出力結果が何であるか推測してください。 1000ミリ秒?

Chrome デバッガーを開きます。下の図を参照してください

ナニ、なぜ 1000 ミリ秒ではないのですか? ! ! !

以下のコードをもう一度見てみましょう:

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

コードを実行した後!

なんてことだ、なぜ私は更新し続けるのですか、ブラウザはスタックしており、警告もありません。 !

論理的には、無限ループしたとしても、1 秒後にはアラートを発する必要があります。

あらゆる種類の問題には同じ理由があります。JavaScript はシングルスレッドです

JavaScript はシングルスレッドであり、setTimeout はマルチスレッドを実装していないことに注意してください。その背後にある真実は次のとおりです:

JavaScript エンジンは単一スレッドで実行され、ブラウザーのみが 1 つのスレッドで JavaScript を実行します。いつでもプログラム。

ブラウザのカーネルは、カーネルの制御下で相互に連携して、JavaScript エンジン スレッド、GUI レンダリング スレッド、ブラウザ イベント トリガーの 3 つの常駐スレッドを実装します。スレッド

*JavaScript エンジン は、イベント駆動型のシングルスレッド実行に基づいており、JavaScript エンジンはタスク キュー内のタスクの到着を常に待機し、それを実行する JavaScript スレッドが 1 つだけあります。いつでもJavaScriptプログラム。

*GUIレンダリングスレッドは、ブラウザインターフェースのレンダリングを担当します。このスレッドは、インターフェースの再描画(Repaint)が必要な場合、または何らかの操作によってリフロー(Reflow)が発生した場合に実行されます。ただし、GUI レンダリング スレッドと JavaScript エンジンは相互に排他的であることに注意してください。JavaScript エンジンが実行されると、GUI スレッドは一時停止され、GUI の更新はキューに保存され、JavaScript が実行されるとすぐに実行されます。エンジンはアイドル状態です。

*イベントトリガースレッド。イベントがトリガーされると、スレッドは保留中のキューの最後にイベントを追加し、JavaScript エンジンによる処理を待ちます。これらのイベントは、setTimeout などの JavaScript エンジンによって現在実行されているコード ブロックから、またはマウス クリックや Ajax 非同期リクエストなどのブラウザ カーネル内の他のスレッドから発生する可能性があります。ただし、JavaScript のシングルスレッド関係により、すべてのイベントが発生します。これらのイベントは、JavaScript エンジンによる処理のためにキューに入れられる必要があります (スレッド内で同期コードが実行されていない場合にのみ、非同期コードが実行されます)。

したがって、上記の説明を通じて、上記の問題はすべて簡単に解決できます。

2. setTimeoutの遅延時間は0です

setTimeoutの遅延時間が0の場合、どのように実行されるかを考えてみてください。

たとえば、次のコード:

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

コードの実行結果は次のようになります:

Javascript シングルスレッドの動作原理をすでに知っていると仮定します。次に、「setTimeout 時間が 0 で、処理キューの最後に追加されていないのに、どうして実行が遅れるのでしょうか?」という疑問が生じるかもしれません。すぐにやるべきではないでしょうか?

私の理解では、setTimeout 時間が 0 であっても、依然として setTimeout であり、原則は変わりません。したがって、キューの最後に追加され、0 秒後に実行されます。

さらに、情報を検索したところ、setTimeoutには最小実行時間があり、指定された時間がその時間未満の場合、ブラウザはsetTimeoutの時間間隔として最小許容時間を使用することがわかりました。 、setTimeout のミリ秒を変更しても、数値は 0 に設定され、呼び出されたプログラムはすぐには開始されません。

最小時間間隔はどれくらいですか?

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

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。