오랫동안 모든 사람들은 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>
코드를 실행하고 크롬 디버거를 열면 다음과 같은 결과를 얻을 수 있습니다
이 결과는 내 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('first time: ' + date.getTime()); //一秒后打印setTimeout里匿名函数的时间 setTimeout(function(){ var date1 = new Date(); console.log('second time: ' + 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>
코드 실행 후!
맙소사, 왜 계속 새로고침을 하고, 브라우저도 멈춰 있고, 경고도 없습니다! !
논리적으로 보면 while을 무한 반복하더라도 1초 후에 경고를 해야 합니다.
모든 문제에는 동일한 이유가 있습니다. JavaScript는 단일 스레드입니다 .
JavaScript는 단일 스레드이며 setTimeout은 다중 스레드를 구현하지 않는다는 사실을 기억하세요.
JavaScript 엔진은 어떤 경우에도 단일 스레드에서 실행됩니다. 브라우저가 있는 곳에는 항상 JavaScript 프로그램을 실행하는 스레드가 하나만 있습니다.
브라우저의 커널은 동기화를 유지하기 위해 커널의 제어 하에 서로 협력합니다. JavaScript. 엔진 스레드, GUI 렌더링 스레드, 브라우저 이벤트 트리거 스레드 .
*JavaScript 엔진은 이벤트 기반 단일 스레드 실행을 기반으로 합니다. JavaScript 엔진은 작업 대기열에 작업이 도착하기를 기다리고 있습니다. 브라우저가 무엇이든 항상 JavaScript 프로그램을 실행하는 JavaScript 스레드는 하나만 있습니다.
*GUI 렌더링 스레드 는 인터페이스를 다시 그려야 하거나(다시 그리기) 리플로우가 필요한 경우 브라우저 인터페이스 렌더링을 담당합니다. 일부 작업(리플로우)으로 인해 스레드가 실행됩니다. 그러나 GUI 렌더링 스레드와 JavaScript 엔진은 상호 배타적이라는 점에 유의해야 합니다. JavaScript 엔진이 실행되면 GUI 스레드가 일시 중지되고 GUI 업데이트가 대기열에 저장되며 JavaScript가 실행될 때 즉시 실행됩니다. 엔진이 유휴 상태입니다.
*이벤트 트리거 스레드 , 이벤트가 트리거되면 스레드는 보류 중인 대기열의 끝에 이벤트를 추가하고 대기합니다. JavaScript 엔진 처리. 이러한 이벤트는 setTimeout과 같은 JavaScript 엔진에 의해 현재 실행되는 코드 블록이나 마우스 클릭, Ajax 비동기 요청 등과 같은 브라우저 커널의 다른 스레드에서 발생할 수 있습니다. 그러나 JavaScript의 단일 스레드 관계로 인해 모든 이벤트는 이러한 이벤트는 JavaScript 엔진의 처리를 위해 대기열에 있어야 합니다(비동기 코드는 스레드에서 동기 코드가 실행되지 않을 때만 실행됩니다).
그래서 위의 설명을 통해 위의 모든 문제를 쉽게 해결할 수 있습니다.
setTimeout의 지연 시간이 0일 때 어떻게 실행될지 생각해 보세요.
예를 들어 다음 코드는
<!DOCTYPE html> <head> <title>setTimeout</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> console.log('a'); setTimeout(function(){ console.log('b'); },0); console.log('c'); console.log('d'); </script> </body> </html>
코드를 실행한 결과는 다음과 같습니다.
Javascript가 단일 스레드에서 어떻게 작동하는지 이미 알고 있다고 가정합니다. 그러면 다음 질문이 생길 수 있습니다. setTimeout 시간은 0이고 처리 대기열 끝에 추가되지 않았습니다. 어떻게 늦게 실행할 수 있습니까? 즉시 해야 하지 않겠습니까?
제가 이해한 바는 setTimeout 시간이 0이더라도 여전히 setTimeout이고 원칙에는 변함이 없다는 것입니다. 따라서 대기열 끝에 추가되어 0초 후에 실행됩니다.
게다가 정보를 검색한 결과 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('container'); var btn = document.getElementById('btn'); //Params: i 为起始高度,num为预期高度 function render(i, num) { i++; con.style.height = i + 'px'; //亮点在此 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('parent'); var child = document.getElementById('child'); parent.onclick = function(){ console.log('parent'); } child.onclick = function(){ //利用setTimeout,冒泡结束后,最后输出child setTimeout(function(){ console.log('child'); },0); } parent = null; child = null; } </script> </body> </html>
执行代码,点击粉红色方块,输出结果:
说到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 = '!!'; var obj = { name:'monkey', 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)!