ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript での setTimeout の単一スレッド

JavaScript での setTimeout の単一スレッド

黄舟
黄舟オリジナル
2017-12-05 13:23:521585ブラウズ

長い間、JavaScript はシングルスレッドであると言われてきましたが、通常は JavaScript プログラムを実行するスレッドは 1 つだけです。ページが更新されて実行が遅れたとき。しかし、多くの JavaScript 初心者は setTimeout の使い方についてまだよく知りません。今日は JavaScript の setTimeout のシングル スレッドを紹介します。

しかし、疑問があるかどうかはわかりません。プログラミング プロセスで使用する 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>
        <div id="container" style="width:100px;height:100px;border:1px solid black;"></div>
        <div id="btn" style="width:40px;height:40px;line-height:40px;margin-top:20px;background:pink;">click</div>
        <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之单线程,相信小伙们对setTimeout有了进一步的了解,希望对你的工作有所帮助!

相关推荐:

JavaScript中setTimeout()的使用详解

JavaScript定时器中关于setTimeout()与setInterval()的详解

javascript函数setTimeout带参数用法实例详解

从setTimeout来谈谈说事件循环模型

以上がJavaScript での setTimeout の単一スレッドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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