>  기사  >  웹 프론트엔드  >  setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

巴扎黑
巴扎黑원래의
2017-04-29 14:43:051512검색

다른 프로그래밍 언어(C#/Java)에서 Javascript로 전환한 개발자로서 Javascript를 학습하는 과정에서 setTimeout() 메소드의 동작 원리를 접하게 되었는데, 이해하기 쉽지 않은 부분이었습니다. 이 기사에서는 이를 다른 프로그래밍 언어와 결합하려고 시도합니다. 구현은 setTimeout 이벤트 루프 모델에서

1. setTimeout부터 시작하겠습니다

setTimeout() 메서드는 ecmascript 사양에 정의되어 있지 않고 BOM에서 제공하는 함수입니다. setTimeout() 메소드에 대한 w3school의 정의를 참조하세요. setTimeout() 메소드는 지정된 밀리초 후에 함수를 호출하거나 표현식을 계산하는 데 사용됩니다.

구문 setTimeout(fn, millisec). 여기서 fn은 실행할 코드를 나타내며 JavaScript 코드 또는 함수가 포함된 문자열일 수 있습니다. 두 번째 매개변수 millisec는 fn이 지연되어야 하는 시간을 나타내는 시간(밀리초)입니다.

setTimeout() 메서드를 호출한 후 메서드는 숫자를 반환합니다. 이 숫자는 시간 초과 호출을 취소하는 데 사용할 수 있는 계획된 실행 코드의 고유 식별자입니다.

처음에는 setTimeout() 사용이 비교적 간단했고 다음 코드

var start = new Date;
setTimeout(function(){
var end = new Date;
console.log('Time elapsed:', end - start, 'ms');
}, 500);
while (new Date - start < 1000) {};

를 보기 전까지는 그 작동 메커니즘을 깊이 이해하지 못했습니다. setTimeout()에 대한 초기 이해에서는 지연이 500ms로 설정되었으므로 출력은 Time elapsed: 500ms여야 합니다. 왜냐하면 직관적으로 이해하면 위의 코드를 실행할 때 자바스크립트 실행 엔진은 위에서 아래로 순차적인 실행 과정을 거쳐야 하고, while 문 이전에 setTimeout 함수가 실행되기 때문입니다. 그러나 실제로 위 코드를 여러 번 실행하면 출력이 최소 1000ms 이상 지연됩니다.

2.Java의 setTimeout 구현

예전에 Java를 배웠던 경험이 생각나서 위에서 언급한 Javascript의 setTimeout()이 나를 혼란스럽게 했습니다. Java에는 setTimeout에 대한 여러 API 구현이 있습니다. 여기서는 java.util.Timer 패키지를 예로 들어 보겠습니다. 타이머를 사용하여 Java에서 위의 논리를 구현합니다. 여러 번 실행한 후 출력은 경과 시간: 501ms입니다.

아아아아

setTimeout()에서 이러한 차이가 발생하는 이유를 살펴보기 전에 먼저 java.util.Timer의 구현 원리에 대해 알아보겠습니다.

위 코드의 핵심 요소는 Timer, TimerTask 클래스와 Timer 클래스의 Schedule 메소드입니다. 관련 소스 코드를 읽어보면 그 구현을 이해할 수 있습니다.

Timer: Task 태스크의 Scheduling 클래스로, TimerTask 태스크와 마찬가지로 사용자가 Schedule 메소드를 통해 Task의 실행 계획을 정리하기 위한 API 클래스입니다. 이 클래스는 TaskQueue 작업 큐 및 TimerThread 클래스를 통해 작업 예약을 완료합니다.

TimerTask: 각 작업이 독립적인 스레드임을 나타내는 Runnable 인터페이스를 구현하고 사용자가 run() 메서드를 통해 자신의 작업을 사용자 정의할 수 있도록 합니다.

TimerThread: Thread에서 상속되며 실제로 Task를 실행하는 클래스입니다.

TaskQueue: Task 작업을 저장하는 데이터 구조로, 힙의 각 멤버는 TimeTask입니다. 각 작업은 TimerTask의 nextExecutionTime 속성 값을 기준으로 정렬됩니다. 가장 먼저 구현되도록 대기열의

3. 결과를 바탕으로 원인 찾기

Java.util.Timer의 setTimeout() 구현을 살펴본 후 Javascript의 setTimeout() 메서드로 돌아가서 이전 출력이 예상과 일치하지 않는 이유를 살펴보겠습니다.

아아아아

코드를 읽어보면 while() 루프 이전에 setTimeout() 메서드가 실행되는 것을 어렵지 않게 알 수 있습니다. 이는 500ms 후에 익명 함수를 한 번 실행하기를 "희망"한다고 선언합니다. 익명 함수의 경우 setTimeout() 메서드에 있으며 실행 후 즉시 적용됩니다. 코드 마지막 줄의 while 루프는 1000ms 동안 계속 실행됩니다. setTimeout() 메서드를 통해 등록된 익명 함수 출력의 지연 시간은 항상 1000ms보다 크며 이는 이 익명 함수에 대한 실제 호출이 다음과 같음을 나타냅니다. while() 루프에 의해 차단됩니다. 실제 호출은 차단 후에 while() 루프가 실제로 실행됩니다.

Java.util.Timer에서는 예약된 작업에 대한 솔루션이 멀티스레딩을 통해 구현됩니다. 작업 개체는 작업 대기열에 저장되고 전용 예약 스레드는 새 하위 스레드에서 작업 실행을 완료합니다. Schedule() 메서드를 통해 비동기 작업을 등록하면 예약 스레드가 즉시 하위 스레드에서 작업을 시작하며 기본 스레드는 작업 실행을 차단하지 않습니다.

이것이 Javascript와 Java/C#, 즉 Javascript의 단일 스레드 메커니즘과 같은 언어의 주요 차이점입니다. 기존 브라우저 환경에서는 Javascript 실행 엔진이 단일 스레드이므로, 실행 엔진은 메인 스레드의 명령문을 실행한 후에만 예약된 작업을 실행합니다. 해당 기간은 작업 등록 시 설정한 지연 시간보다 길어질 수 있습니다. 이 시점에서 Javascript와 Java/C#의 메커니즘은 매우 다릅니다.

4. 이벤트 루프 모델

단일 스레드 Javascript 엔진에서 setTimeout()은 어떻게 작동합니까? 여기서는 브라우저 커널의 이벤트 루프 모델에 대해 언급하겠습니다. 간단히 말하면 Javascript 실행 엔진 외부에는 작업 대기열이 있는데, 코드에서 setTimeout() 메서드가 호출되면 등록된 지연 메서드가 브라우저 커널의 다른 모듈로 전달됩니다(웹킷을 예를 들어 webcore 모듈입니다) 처리를 하면, 지연 방식이 트리거 조건에 도달하면, 즉 설정된 지연 시간에 도달하면 이 지연 방식이 작업 대기열에 추가됩니다. 이 프로세스는 브라우저 커널의 다른 모듈에 의해 처리되며 실행 엔진의 메인 스레드와 독립적입니다. 메인 스레드 메서드의 실행이 완료되고 실행 엔진이 유휴 상태에 도달한 후 순차적으로 작업에서 작업을 가져옵니다. 이 프로세스는 연속 루프입니다. 이 프로세스를 이벤트 루프 모델이라고 합니다.

위의 이벤트 루프 모델을 연설의 정보를 참조하면 다음 그림으로 설명할 수 있습니다.

Javascript 실행 엔진의 메인 스레드가 실행되면 힙과 스택이 생성됩니다. 프로그램 내의 코드는 하나씩 스택에 들어가 실행을 기다립니다. setTimeout() 메소드가 호출되면, 즉 그림 오른쪽의 WebAPIs 메소드가 호출되면 브라우저 커널의 해당 모듈이 시작됩니다. 지연 메서드 처리 지연 메서드가 트리거 조건에 도달하면 메서드는 콜백을 위해 작업 대기열에 추가됩니다. 실행 엔진 스택의 코드가 실행되는 한 기본 스레드는 작업 대기열을 읽고 해당 콜백을 실행합니다. 트리거 조건을 순차적으로 충족하는 기능입니다.

​연설의 예를 사용하여 더 자세히 설명하세요.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

그림의 코드를 예로 들면, 실행 엔진이 위 코드를 실행하기 시작하면 실행 스택에 main() 메서드를 추가하는 것과 같습니다. console.log('Hi')를 계속 시작하면 log('Hi') 메서드가 스택에 푸시됩니다. console.log 메서드는 WebAPI에 포함된 메서드가 아니라 웹킷 커널에서 지원하는 일반적인 메서드입니다. 이전 그림이므로 여기에 로그('Hi') 메서드가 즉시 스택에서 제거되고 엔진에 의해 실행됩니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

console.log('Hi') 문이 실행된 후 log() 메서드가 스택에서 팝되고 Hi가 출력됩니다. 엔진은 계속 작동 중지되고 실행 스택에 setTimeout(callback,5000)을 추가합니다. setTimeout() 메소드는 이벤트 루프 모델의 WebAPI에 포함된 메소드에 속하며, 엔진이 실행을 위해 스택에서 setTimeout() 메소드를 꺼내면 해당 모듈, 즉 타이머 모듈에 지연 실행 기능을 넘겨줍니다. 처리를 위해 그림의 오른쪽에 있습니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

실행 엔진이 실행을 위해 스택에서 setTimeout을 Pop하면 지연 처리 방법을 웹킷 타이머 모듈에 넘긴 후 즉시 다음 코드를 계속 처리하므로 실행 스택에 log('SJS')가 추가되고, 그런 다음 log('SJS' )가 스택 외부에서 실행되어 SJS를 출력합니다. 실행 엔진이 console.log('SJS')를 실행한 후 프로그램이 처리되고 main() 메서드도 스택에서 제거됩니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

이때, setTimeout 메소드가 실행된 후 5초가 지나면 타이머 모듈은 지연 처리 메소드가 트리거 조건에 도달한 것을 감지하여 해당 지연 처리 메소드를 작업 큐에 추가합니다. 이때 실행 엔진의 실행 스택이 비어 있으므로 엔진은 작업 큐에 실행해야 할 작업이 있는지 확인하기 위해 폴링을 시작합니다. 지연 메서드가 실행 조건에 도달했음을 감지하여 추가합니다. 실행 스택에 대한 지연 방법. 엔진은 지연 메서드가 log() 메서드를 호출한다는 것을 발견하여 log() 메서드를 스택에 푸시했습니다. 그런 다음 실행 스택이 차례로 팝업되어 거기에 출력되고 실행 스택이 지워집니다.

실행 스택을 지운 후 실행 엔진은 계속해서 작업 큐를 폴링하여 실행할 수 있는 작업이 아직 있는지 확인합니다.

 5.webkit中timer的实现

  到这里已经可以彻底理解下面代码的执行流程,执行引擎先将setTimeout()方法入栈被执行,执行时将延时方法交给内核相应模块处理。引擎继续处理后面代码,while语句将引擎阻塞了1秒,而在这过程中,内核timer模块在0.5秒时已将延时方法添加到任务队列,在引擎执行栈清空后,引擎将延时方法入栈并处理,最终输出的时间超过预期设置的时间。

var start = new Date;
setTimeout(function(){
var end = new Date;
console.log(&#39;Time elapsed:&#39;, end - start, &#39;ms&#39;);
}, 500);
while (new Date - start < 1000) {};

  前面事件循环模型图中提到的WebAPIs部分,提到了DOM事件,AJAX调用和setTimeout方法,图中简单的把它们总结为WebAPIs,而且他们同样都把回调函数添加到任务队列等待引擎执行。这是一个简化的描述,实际上浏览器内核对DOM事件、AJAX调用和setTimeout方法都有相应的模块来处理,webkit内核在Javasctipt执行引擎之外,有一个重要的模块是webcore模块,html的解析,css样式的计算等都由webcore实现。对于图中WebAPIs提到的三种API,webcore分别提供了DOM Binding、network、timer模块来处理底层实现,这里还是继续以setTimeout为例,看下timer模块的实现。

  Timer类是webkit 内核的一个必需的基础组件,通过阅读源码可以全面理解其原理,本文对其简化,分析其执行流程。

setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.

  通过setTimeout()方法注册的延时方法,被传递给webcore组件timer模块处理。timer中关键类为TheadTimers类,其包含两个重要成员,TimerHeap任务队列和SharedTimer方法调度类。延时方法被封装为timer对象,存储在TimerHeap中。和Java.util.Timer任务队列一样,TimerHeap同样采用最小堆的数据结构,以nextFireTime作为关键字排序。SharedTimer作为TimerHeap调度类,在timer对象到达触发条件时,通过浏览器平台相关的接口,将延时方法添加到事件循环模型中提到的任务队列中。

  TimerHeap采用最小堆的数据结构,预期延时时间最小的任务最先被执行,同时,预期延时时间相同的两个任务,其执行顺序是按照注册的先后顺序执行。

var start = new Date;
setTimeout(function(){
console.log(&#39;fn1&#39;);
}, 20);
setTimeout(function(){
console.log(&#39;fn2&#39;);
}, 30);
setTimeout(function(){
console.log(&#39;another fn2&#39;);
}, 30);
setTimeout(function(){
console.log(&#39;fn3&#39;);
}, 10);
console.log(&#39;start while&#39;);
while (new Date - start < 1000) {};
console.log(&#39;end while&#39;);

  上述代码输出依次为

start while
end while
fn3
fn1
fn2
another fn2

 参考资料

  1.《Javascript异步编程》

  2.JavaScript 运行机制详解:再谈Event Loophttp://www.ruanyifeng.com/blog/2014/10/event-loop.html

  3.Philip Roberts: Help, I'm stuck in an event-loop.https://vimeo.com/96425312

  4.How JavaScript Timers Work.http://ejohn.org/blog/how-javascript-timers-work/

  5.How WebKit’s event model works.http://brrian.tumblr.com/post/13951629341/how-webkits-event-model-works

  6.Timer实现.http://blog.csdn.net/shunzi__1984/article/details/6193023

위 내용은 setTimeout의 이벤트 루프 모델에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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