>  기사  >  웹 프론트엔드  >  JavaScript의 setTimeout 단일 스레드

JavaScript의 setTimeout 단일 스레드

黄舟
黄舟원래의
2017-12-05 13:23:521593검색

오랫동안 모든 사람들은 Javascript가 단일 스레드라고 말해왔습니다. 브라우저의 시간에 관계없이 JavaScript 프로그램을 실행하는 스레드는 하나만 있습니다. 일반적으로 JavaScript의 setTimeout 메서드가 더 유용합니다. 페이지가 새로 고쳐지고 실행이 지연될 때. 그런데 아직 많은 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>

코드를 실행하고 크롬 디버거를 열면 다음과 같은 결과를 얻을 수 있습니다.

이 결과는 내 setTimeout의 콘텐츠가 실행되기 때문에 이해하기 쉽습니다. 물론 100ms 후에 a를 먼저 출력하고, 그다음 c를 출력하고, 100ms 후에 setTimeout에서 b를 출력하는 것입니다.

자바스크립트는 이제 싱글스레드가 아닌가요? 멀티스레드도 가능한가요? ! !

사실은 아닙니다. 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밀리초?

크롬 디버거를 열면 아래 사진을 보세요

나니 왜 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 엔진은 단일 스레드에서 실행되며 브라우저에는 언제든지 JavaScript를 실행하는 스레드가 하나만 있습니다. .

브라우저의 커널은 동기화를 유지하기 위해 커널의 제어 하에 서로 협력합니다. 브라우저는 JavaScript 엔진 스레드, GUI 렌더링 스레드, 브라우저 이벤트 트리거링 등 세 가지 이상의 상주 스레드를 구현합니다. 스레드 .

*JavaScript 엔진은 이벤트 기반 단일 스레드 실행을 기반으로 합니다. JavaScript 엔진은 항상 작업 대기열에 작업이 도착할 때까지 기다린 후 이를 처리합니다. 언제든지 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.