JavaScript의 Event Loop

koreanhole·2021년 2월 12일
0

JavaScript

목록 보기
1/1

JavaScript의 작동 원리

C, Java 그리고 Python과 같은 동기적 언어는 별도의 스레드나 프로세스를 사용하지 않는 이상 먼저 작성된 순서대로(동기적으로) 코드가 실행된다. 나중에 작성된 코드가 먼저 작성된 코드보다 먼저 실행될 일은 없다는 것이다.

하지만 JavaScript는 비동기적 언어이다. JavaScript는 기본적으로 싱글 스레드로 작동한다. 하나의 메인 스레드로만 구성되어 있으며 한번에 하나의 작업만 수행할 수 있다. 다른 작업이 중간에 끼어들 수도 없고, 기존에 수행하던 작업이 끝나야만 그 다음 작업을 수행할 수 있다. 이러한 JavaScript의 비동기성에 의해 먼저 실행된 코드의 작업이 끝나기 전에 나중에 실행된 코드의 작업이 먼저 끝날 수도 있다.

function first() {
  setTimeout(() => {
    console.log("The First function has been called.");
  }, 1000);
}

function second() {
  setTimeout(() => {
    console.log("The Second function has been called.");
  }, 500);
}

first();
second();

위에서 first() 가 먼저 호출되었고 second() 가 나중에 호출되었다.

하지만 위 코드의 실행결과는 다음과 같다.

The Second function has been called.
The First function has been called.

실제 first()second() 의 실행 순서가 호출 순서와 다른지를 이해하기 위해서는 JavaScript의 콜 스택과 이벤트 루프에 대해서 알아야 한다.

콜 스택(Call Stack)

자바스크립트는 스택의 형태인 콜 스택에 호출된 함수를 추가하고(Push) 실행된 함수를 제거(Pop)한다.

function foo(b) {
  var a = 10;
  return a + b + 11;
}

function bar(x) {
  var y = 3;
  return foo(x * y);
}

console.log(bar(7));

bar() 를 호출하면서 bar 의 인자와 지역 변수를 포함하는 프레임이 콜 스택에 push된 후 이어서 barfoo 를 호출하면서 foo 의 인자와 지역 변수를 포함하는 프레임이 이어서 콜 스택에 push된다.

foo 의 실행이 끝나면 foo 는 콜 스택에서 pop되고 이어서 bar 프레임도 콜 스택에서 pop되면서 콜 스택이 비워지게 된다.

bar호출 → foo호출 → foo종료 → bar종료

콜 스택이 무엇인지 알게 되었으니 위에서 나왔던 코드를 다시 한 번 보자.

function first() {
  setTimeout(() => {
    console.log("The First function has been called.");
  }, 1000);
}

function second() {
  setTimeout(() => {
    console.log("The Second function has been called.");
  }, 500);
}

first();
second();

JavaScript는 오직 하나의 콜 스택을 갖고 있다고 했는데 어떻게 first()second()setTimeout(() ⇒ {}, time) 이 동시에 관리되는 것일까?

사실 위 사진과 같이 Memory Heap과 Call Stack을 포함한 JavaScript엔진 이외에도 자바스크립트 실행에 관여하는 요소들이 존재한다. Wep API와 Event Loop 그리고 Callback Queue가 그것이다.

setTimeout 을 실행하면 자바스크립트 엔진은 setTimeout의 callback함수와 time을 Web API에 전송한다. 이후 Web API는 setTimeout 작업을 실행하며 설정된 시간(1000ms or 500ms)이 경과하면 callback함수(console.log(...))를 Callback Queue에 전달한다.

콜백 큐(Callback Queue) & 이벤트 루프(Event Loop)

event loop은 콜 스택이 비워질때까지 콜백 큐에 있는 콜백 함수(메시지)의 처리를 계속한다.

이벤트 루프의 구현 방식은 다음과 같다.

while (queue.waitForMessage()) {
  queue.processNextMessage();
}

이벤트 루프는 큐에 새로운 메시지가 있을 때마다 다음 메시지를 처리하는 것이다.

만약 큐에 아무런 메시지가 없다면 새로운 메시지의 도착을 동기적으로 기다린다.

Run to completion scheduling(nonpreemptive scheduling)

자바스크립트의 콜백 큐에서는 현재 메시지가 완전히 처리된 후에야 다음 메시지가 처리되기 시작한다.

만약 어떤 메시지가 지나치게 오랜 시간동안 처리되는 중이라면 웹 브라우져는 다음과 같은 오류 화면을 내보낸다.

위와 같은 상황이 일어나지 않게 하려면 메시지 처리를 되도록 짧게 만드는 것과 하나의 메시지를 여러개의 메시지로 나누어야 한다.

자바스크립트는 setTimeout의 두번째 인수인 시간값의 지연을 보장하지 않는다. 단지 시간의 최소 지연을 나타낼 뿐이다.

이유는 자바스크립트의 Run to completion scheduling 때문이다.

event loop은 Run to completion scheduling에 근거하여 큐에 대기중인 작업들은 순차적으로 모두 처리 한 후 콜백 메시지를 처리하기 때문이다.

참고자료

Javascript 동작원리 (Single thread, Event loop, Asynchronous)

비동기적 Javascript - 싱글스레드 기반 JS의 비동기 처리 방법 - Hudi - 유사 프로그래머

동시성 모델과 이벤트 루프

0개의 댓글