JavaScript의 setTimeout에 관한 사항

高洛峰
풀어 주다: 2016-12-07 09:52:49
원래의
1203명이 탐색했습니다.

1. setTimeout의 단일 스레드

오랫동안 모든 사람들은 Javascript가 단일 스레드라고 말해왔습니다. 브라우저에는 언제든지 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>
로그인 후 복사

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

JavaScript의 setTimeout에 관한 사항

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

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

사실은 그렇지 않아요. 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밀리초?

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

JavaScript의 setTimeout에 관한 사항

나니 왜 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>
로그인 후 복사

코드 실행 후!

왜 계속 새로고침이 되고, 브라우저가 멈췄으며, 경고도 없습니다! !

논리적으로 보면 while 무한 루프를 해도 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에 관한 사항

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>
  <div id="parent">
   <div id="child"></div>
  </div>
  <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>
로그인 후 복사

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

JavaScript의 setTimeout에 관한 사항

三、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调试器,查看输出结果:

JavaScript의 setTimeout에 관한 사항

咦,它怎么输出的是”!!”呢?不应该是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();
로그인 후 복사

   


관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿