Event Loop 동작과정 최종정리


:raising_hand: 프론트엔드 개발관련 공부내용을 기록하는 포스트 입니다.


image

1. 개요


Javscript 는 생각보다 복잡하게 동작한다.

Javscript Engine 은 기본적으로 단일 스레드(Single Thread) 방식으로 동작한다.

하지만, 이러한 상황에서 그럼 Browser에서는 굉장히 많은 작업들이 동시에 일어나는 것은 어떻게 진행되는 것일까?

그 부분에 대한 답변이 Event Loop 이며 이에 대한 동작과정을 포스팅해보도록 한다.

2. 단일 스레드


Javscript 는 앞서 말했듯이 단일 스레드(Single Thread)로 동작한다.

즉, 한번에 하나의 작업만 실행할 수 있다.

예를들어, 단일 스레드로 동작한다면 우리가 30초가 걸리는 작업이 실행되면 그외 어떠한 작업도 30초동안은 어떠한 작업도 진행할 수 없다는 이야기 이다.

하지만, 현재의 웹 브라우저 생태계에서 이러한 일은 절대 있을 수 없다.

따라서, JavscriptEvent Loop를 통해서 이를 해결하는 것이다.

3. Event Loop 란?


image

Javascript Engine단일 스레드로 작업이 진행되지만 위와 같은 문제를 해결하기 위해 브라우저Web API를 제공한다.

여기에는 DOM API, setTimeout, HTTP, Ajax요청 등이 있을 수 있다.

즉, Event Loop 는 크게 Javscript Engine, WebAPI, Callback Queue 이렇게 3가지의 조합으로 동작한다고 보면 될 것이다.

그럼 동작과정을 한번 확인해보자.

Javscript Engine 이 코드를 읽어내려 가면서 함수를 호출할 경우 Call Stack 에 해당 함수를 넣게 된다.

스택의 경우 후입 선출이며 아래와 같이 동작할 것이다.

image

Javscript Engine 은 코드를 읽어내려가다가 greet() 함수를 만나고 Call Stack 에 넣은 후 실행한 후 , respond() 를 넣고 setTimeout() Timer가 실행된다.

image

이때 위에서 설명했듯이, Javscript Engine 은 해당 Timer 함수를 브라우저가 제공하는 Web API에 전달하고 작성한 1000ms 가 될때 까지 해당 공간에서 대기한다.

그렇게 되면 Javscript Engine1000ms의 시간동안 다시 코드를 읽어 내려가며 동일한 방식으로 코드를 실행하게 된다.

image

그러던 중 1000ms 가 지나게 되면 Web API는 대기하던 Timer 함수의 Call back함수는 Call back Queue 로 전달되게 된다.

그러면 현재까지의 상황을 정리해보자.

현재 Javscript Engine 은 계속해서 코드를 읽어내려가며 Call Stack에 함수를 넣어 실행해가고 있는 상황이며 드디어 조건이 완료된 TimerCall back 함수가 Call back Queue 에 있는 상황이다.

이때부터가 Event Loop 의 기능이 적용되는 상황이다.

image

Event LoopJavscript EngineCall Stack을 모니터링 하다가 Call Stack이 모두 비워지는 순간이 오면 Call back Queue 에서 대기하고 있는 Call back 함수를 Call Stack으로 옮겨주는 역할을 한다.

이때, 우리가 주의해야 할 부분은 Javscript EngineRun-to-Complete 방식으로 실행된다.

이는 하나의 함수가 실행되면 끝날 때까지는 어떠한 작업도 끼어들지 못한다는 의미이다.

즉, 이러한 방식은 자바스크립트 엔진은 단일 호출 스택 즉, 하나의 호출 스택을 사용하며, 하나의 작업이 시작되어 스택에 쌓여 있으면 스택의 모든 작업들이 실행을 마치고 스택이 비워지기 전까지 어떠한 함수도 실행될 수 없다는 것을 의미한다.

image

그렇게 되면 Javascript Engine 은 읽던 코드가 아닌 Event Loop에 의해 전달된 Call back 함수를 실행하는 것이다.

3-1. 실습

그럼 조금 더 이해하기 위해 아래와 같은 코드의 결과값을 유추해보자.

function delay() {
  for (var i = 0; i < 100000; i++);
}
function foo() {
  delay();
  bar();
  console.log("foo!");
}
function bar() {
  delay();
  console.log("bar!");
}
function baz() {
  console.log("baz!");
}

setTimeout(baz, 10);
foo();
  1. Javascript 에서 해당 코드를 확인하면 실행 컨텍스트를 통해 소스의 `렉시컬 환경을 구성하게 될 것이다.
  2. 코드를 읽어내려가면 첫번쨰 setTimeout(baz, 10) 함수를 실행하면, Call Stack에 들어간다.
  3. Javscript EngineTimer 를 실행하기 위해 Web API로 전달하게 되며 그다음 foo() 함수를 실행한다.
  4. foo() 함수 내 delay() 함수를 먼저 실행하고 bar() 함수가 실행되고 delay() 함수를 실행후 완료되면 bar! 가 콘솔에 추가될 것이다.
  5. 이후 foo! 로그가 추가되고 foo함수가 종료되면서 Call Stack이 비워질 것이다.
  6. Event Loop는 이때를 확인하여 Call back Queue에 대기하고 있던 baz() 함수를 Call Stack으로 옮기고 Javscript Engine은 이를 실행하며 마지막으로 baz!가 출력되며 코드가 종료될 것이다.

Call Stack Queue 의 종류


image

Call back이 들어오는 Queue에는 3가지 로 확인된다.

  1. Task Queue
  • setTimeout, setInterval 등의 비동기 함수가 포함된다.
  1. Microtask Queue
  • Promise Callback, async Callback 등의 비동기 함수가 포함된다.
  1. Animation Frames
  • requestAnimationFrame과 같은 애니메이션관련 비동기 함수가 포함된다.

여기서 중요한 부분은 각 Queue 마다 우선순위가 다르다는 것이다.

예를들어 Call stack이 비었을 때 Event LoopQueue를 보았는데 Task , Microtask, Animation Queue 에 모두 Call back 함수가 대기하고 있다고 가정하자.

이때, Event LoopMicrotask Queue > Animation Frame > Task Queue 순으로 동작한다는 점을 알아야 한다.

4. 정리


결국 이벤트 루프가 무엇일까? 에 대한 정답은 아래와 같을 것이다.

이벤트 루프는 싱글 스레드 언어인 Javascript가 코드의 동시성을 보장하기 위한 기능을 수행하는 요소이다.

Javscript Engine은 코드를 실행하면서 Call Stack에 넣으며, 비동기 함수의 경우 Web API 로 이동시켜 조건을 만족하면 Call back Queue로 전달해 Javascript EngineCall Stack이 비워질 때 까지 대기를 합니다.

이후 Call Stack이 비워지면 Event LoopCall back Queue를 확인하고 각 Queue의 순서를 보장하면서 대기하던 Call back 함수들을 Call Stack으로 이동시켜 실행하게 됩니다.

Call back Queue에는 서로다른 역할을 하는 Queue가 존재하며 setTimeout, setInterval 등을 담는 Task Queue, Promise, asycnc 등의 Call back함수를 담는 MicroTask Queue, requestAnimationFrame 과 같은 Call back을 담는 Animation Frame 이 존재한다.

Microtask Queue > Animation Frame > Task Queue 순으로 우선순위를 잡고 Event LoopCall Stack이 비었을 경우 우선순위에 따라 순차적으로 Queue에서 대기중인 함수를 옮겨서 실행한다.

참조 사이트