[번역] JavaScript Event Loop Explained

원문 : https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

"JavaScript는 어떻게 비동기로 동작하고 단일 스레드 인가요?" 에 대한 짧은 답변으로는 JavaScript 언어가 단일 스레드이며, 비동기 동작은 JavaScript 언어 자체의 일부가 아니라 브라우저(또는 프로그래밍 환경)의 코어 JavaScript 언어 환경에서 구축되며 브라우저 API를 통해 엑세스 됩니다.

좀 더 자세한 답변을 위해 2 개의 코드 스니펫을 사용하겠습니다.

기본 구조 (Basic Architecture)

  • 힙 (Heap) - 객체는 대부분 구조화되지 않은 큰 메모리 영역인 힙에 할당됩니다.

  • 스택 (Stack) - JavaScript 코드 실행을 위해 제공되는 단일 스레드를 의미합니다. 함수 호출은 프레임 스택을 형성합니다.(자세한 내용은 아래 참조)

  • 브라우저 혹은 웹 API (Browser or Web API)는 웹 브라우저 안에 내장되어 있으며 브라우저 및 주변 퓨터 환경에서 데이터를 노출하고 유용하고 복잡한 작업을 수행할 수 있습니다. JavaScript 언어 자체의 일부가 아니라 코어 JavaScript 언어 위에 구축되어 JavaScript 코드에서 사용할 수 있는 추가 기능을 제공합니다. 예를 들어, Geolocation API는 위치 데이터 검색을 위한 간단한 JavaScript 구성을 제공해서 Google 지도에서 위치를 플롯할 수 있습니다. 백그라운드에서 실제로 장치의 GPS 하드웨어(또는 위치데이터를 결정하는데 사용하는 모든 것)와 통신하고 데이터를 검색하여 브라우저 환경으로 돌려 보내기 위해 복잡한 하위 레벨 코드(예: C++)를 사용하고 있습니다. 하지만 우리의 코드에서 이 복잡성은 API에 의해 추상화 됩니다.

Code Snippet 1 : Intrigue the mind

function main(){
  console.log('A');
  setTimeout(
    function display(){ console.log('B'); }
  ,0);
	console.log('C');
}
main();
//	Output
//	A
//	C
//  B

여기에는 콘솔에 'A'와 'C'를 출력하는 2 개의 console.log 명령어를 가진 main 함수가 있습니다. 두 명령어 사이에 0ms 대기시간으로 콘솔에 'B'를 출력하는 setTimeout 호출이 있습니다.

  1. main 함수 호출은 먼저 스택으로(프레임으로) 푸시됩니다. 그 다음 브라우저는 기본 함수의 첫 번째 명령문인 console.log('A')를 스택으로 푸시합니다. 이 명령문이 실행되고 완료되면 해당 프레임이 튀어 나옵니다. 콘솔에 알파벳 A가 출력됩니다.

  2. 다음 명령문 (콜백 exec() 및 0ms 대기 시간이 있는 setTimeout())이 호출 스택으로 푸시되고 실행이 됩니다. setTimeout 함수는 브라우저 API를 이용하여 제공된 함수의 콜백을 지연시킵니다. 브라우저로 이동이 (타이머에 대한) 완료되면 프레임 (setTimeout 포함)이 튀어 나옵니다.

  3. exec() 함수의 콜백을 위해 브라우저에서 타이머가 실행되는 동안 console.log('C')가 스택으로 푸시됩니다. 이때 정해진 지연이 0ms이므로 브라우저가 (이상적으로) 수신하자마자 콜백이 메시지 큐에 추가됩니다.

  4. main 함수에서 마지막 명령문을 실행한 후 main() 프레임이 호출 스택에서 튀어나와 스택이 비워집니다. 브라우저가 큐에서 호출 스택으로 메시지를 푸시하려면 먼저 호출 스택이 비어있어야 합니다. 따라서 setTimeout() 에 제공된 지연 시간이 0초여도 exec() 에 대한 콜백은 호출 스택의 모든 프레임 실행이 완료될 때까지 기다려야 합니다.

  5. 이제 콜백 exec() 가 호출 스택으로 푸시되어 실행됩니다. 알파벳 B가 콘솔에 출력됩니다. 이것이 JavaScript의 이벤트 루프입니다.

따라서 setTimeout(function, delayTime) 의 지연 매개 변수는 함수가 실행 된 후 정확한 시간 지연을 보장하지 않습니다. 특정 상황에서 함수가 실행될 최소 대기 시간을 나타냅니다.

Code Snippet 2 : Deeper Understanding

function main(){
  console.log('A');
  setTimeout(
    function exec(){ console.log('B'); }
  , 0);
  runWhileLoopForNSeconds(3);
  console.log('C');
}
main();
function runWhileLoopForNSeconds(sec){
  let start = Date.now(), now = start;
  while (now - start < (sec*1000)) {
    now = Date.now();
  }
}
// Output
// A
// C
// B
  • runWhileLoopForNSeconds() 함수는 이름처럼 정확하게 수행합니다. 호출된 시간부터 경과 된 시간이 인수로 전달받은 초수와 같은지 계속 확인합니다. 기억해야할 주요 사항은 while 루프(다른 것들과 마찬가지로)는 호출 스택에서 실행이 발생하고 브라우저 API를 사용하지 않는 차단 문이라는 점입니다. 따라서 실행이 끝날 때까지 모든 후속 명령문을 차단합니다.

  • 따라서 위 코드의 setTimeout의 지연 시간이 0초이고 while 루프가 3초 동안 실행되더라도 exec() 콜백은 메시지 큐에서 멈춰있습니다. while 루프는 3초가 경과할 때 까지 호출 스택(단일 스레드)에서 계속 실행됩니다. 그리고 호출 스택이 비워지면 콜백 exec()이 호출 스택으로 이동하여 실행됩니다.

  • setTimeout의 지연 인수는 타이머가 지연을 완료한 후 실행 시작을 보장하지 않습니다. 지연의 최소 시간으로 사용됩니다.

Last updated