[JavaScript] 이벤트 루프

Suyoung·2022년 2월 3일
0
post-thumbnail

개요

자바스크립트의 특징 중 하나를 꼽으라면, '싱글 스레드' 기반의 언어라는 점이 있다. 이는 곧 한 번에 하나의 작업만을 처리할 수 있음을 의미한다. 하나의 함수가 실행 중이라면, 해당 함수의 실행이 끝나기 전에는 다른 어떤 작업도 끼어들 수 없다.

하지만, 실제로 자바스크립트가 사용되는 환경에서는 많은 작업이 동시에 처리되고 있다. 웹 브라우저는 애니메이션 효과를 보여줌과 동시에 마우스 클릭 입력을 받아 처리하고, Node.js 기반의 웹 서버에서는 동시에 여러 개의 HTTP 요청을 처리하기도 한다.

이러한 일이 가능하기 위해서는 동시성(Concurrency)을 지원해야 하는데, 자바스크립트는 어떻게 동시성을 지원할까?

이벤트 루프

이 때 등장하는 개념이 이벤트 루프이다. 자바스크립트는 이를 이용해서
비동기 방식으로 동시성(Concurrency)을 지원한다.

먼저 브라우저 환경을 간단한 그림을 통해 확인해보자.

browser_environment

위 그림을 통해 setTimeout(Timer), XMLHttpRequest(AJAX) 등의 비동기 호출을 위한 함수는 Web API에 정의되어 있고, 이벤트 루프와 태스크 큐 또한 자바스크립트 엔진 외부에 구현되어 있음을 알 수 있다.

동작 방식

함수를 호출할 때, 이는 호출 스택(Call Stack)이라는 곳에 추가(push)된다. 자바스크립트 엔진의 일부로, 이름 그대로 스택 자료구조이다. 함수가 값을 반환하면, 해당 함수는 스택에서 제거(pop)된다.

자바스크립트가 '싱글 스레드' 기반 언어라는 것은 자바스크립트 엔진이 하나의 호출 스택(Call Stack)만을 사용한다는 것을 의미한다.

위에서 respond 함수는 setTimeout을 반환한다.
Web API에 정의된 setTimeout을 활용해서, 메인 스레드를 중단(block)시키지 않고도
태스크를 지연시키는 것이 가능하다.
respondsetTimeout 은 각각 값을 반환하고 나서 호출 스택에서 제거된다.

setTimeout에 전달한 콜백 함수 () => { return "Hey!" } 는 Web API에 추가된다.
Web API에서, setTimeout 의 두 번째 인자로 전달한 값(ms)만큼 타이머가 작동한다.
위 예시에서는 1000을 전달하였으므로, 1000ms 동안 타이머가 작동한다.

타이머의 작동이 끝나면, 콜백 함수는 호출 스택에 곧바로 추가되지 않고, 태스크 큐(그림에서는
QUEUE로 표현됨)로 전달된다. (이 부분이 혼란을 불러일으킨다)
setTimeout의 두 번째 인자로 전달한 값 1000은 setTimeout
'콜백 함수'가 1000ms 후에 호출 스택에 push되는 것을 의미하는 것이 아니라,
'1000ms 후에 태스크 큐로 전달되는 것'을 의미한다.

태스크(또는 매크로태스크) 큐는 이름 그대로 콜백 함수와 같은 태스크들이 자신이 실행될 차례를 기다리는 큐이다. 자세한 내용은 링크를 참고.

이제, 이벤트 루프가 동작한다. 이벤트 루프는 태스크 큐를 호출 스택에 연결한다.
만약 호출 스택이 비어있다면(호출 스택에 존재하던 모든 함수가 각각의 값을 반환하고,
스택에서 제거되었다면), 이벤트 루프는 태스크 큐의 첫 번째 태스크를 꺼내 호출 스택에 추가한다.

호출 스택에 추가된 콜백 함수가 실행되고, 값을 반환한 후 호출 스택에서 제거된다.

예제

다음 코드의 실행 결과를 예상해보자.

const foo = () => console.log("First");
const bar = () => setTimeout(() => console.log("Second"), 500);
const baz = () => console.log("Third");

bar();
foo();
baz();

First > Third > Second 순서로 콘솔에 출력된다.

실행 결과가 이해가 잘 되지 않는다면, 그림을 통해 코드의 실행 흐름을 알아보자.

  1. bar 함수가 실행된다. bar 함수는 setTimeout을 반환한다.
  2. setTimeout에 전달된 콜백 함수는 Web API에 추가되고, bar 함수와 setTimeout
    값을 반환했으므로 호출 스택에서 제거된다.
  3. 타이머가 작동하고, 그동안 foo 함수가 실행되어 First가 콘솔에 출력된다.
    foo 함수는 return문이 없으므로 undefined 를 반환하고, 호출 스택에서 제거된다.
  4. baz 함수가 실행되고, Third 가 콘솔에 출력된 후 호출 스택에서 제거된다.
    타이머의 작동이 종료되었으므로, 콜백 함수는 태스크 큐로 전달된다.
  5. 이벤트 루프는 호출 스택이 비어있음을 확인하고, 태스크 큐의 첫 번째 태스크(콜백 함수)를
    호출 스택에 추가한다.
  6. 호출 스택에 추가된 콜백 함수가 실행되고, Second가 콘솔에 출력되고 호출 스택에서 제거된다.

References

JavaScript Visualized: Event Loop
[번역] 자바스크립트 이벤트 루프
자바스크립트와 이벤트 루프(NHN Cloud Meetup)
이벤트 루프와 매크로 · 마이크로태스크
코드스피츠 85 거침없는 자바스크립트 - 1회차 : Flow Control 파트 (1:21:20~)

profile
블로그 이전 >> suyoung.vercel.app

0개의 댓글