Javascript와 eventloop

Creating the dots·2022년 1월 9일
0

CS

목록 보기
10/19
post-custom-banner

자바스크립트 엔진은 Memory Heap과 Call Stack을 제공한다.

여기서 자바스크립트 엔진은 자바스크립트 코드를 컴퓨터가 이해할 수 있도록 해석하고 실행하는 interpreter이다.

Memory Heap

  • 배열, 객체, 함수 등의 참조타입은 메모리 힙에 저장된다.
  • 사용이 끝난 memory heap의 데이터는 garbage collector에 의해 수거된다.

Call Stack

  • 자바스크립트는 하나의 Call Stack을 가지고 있고, 그 뜻은 한 번에 하나의 일만 처리할 수 있다는 뜻이다.
  • 원시타입 데이터를 저장한다.
  • 프로그램에서 실행되고 있는 자바스크립트 코드의 위치를 기록한다. 차례로 쌓여서 현재 실행되고 있는 코드가 어디인지 확인할 수 있다. 자료구조가 Stack인 만큼 LIFO(Last In First Out)의 특성을 갖는다.
  • Call Stack Overflow
    재귀를 호출했을때 스택의 사이즈를 초과해 발생하는 오류이다.
  function foo() {
    foo();
  }
  foo();

여기까지만 보면 자바스크립트는 하나의 Call Stack을 가지고, 그 Call Stack에 함수들이 쌓여 위에서부터 차례로 실행되므로 동기적으로 동작하는 것으로 보인다.

다음 첫번째 예시코드를 살펴보자.

const response = ajax('https://example.com/api');
console.log(response); //undefined

실행결과 콘솔에 undefined가 찍히는 것을 알 수 있다. 동기적으로 동작한다면, ajax 요청이 끝날때까지 console.log(response)는 실행되지 않아야한다.

그런데 왜 ajax 요청이 끝나기 전에 response가 찍힌걸까?🤔

두번째 예시코드를 살펴보자.

function first() {
  console.log('first');
}
function second() {
  console.log('second');
}
function third() {
  console.log('third');
}
first();
setTimeout(second, 1000);
third();

//first
//third
//second

여기서도 setTimeout 함수가 끝나기 전에 third 함수가 먼저 실행된 것을 확인할 수 있다.

자바스크립트 런타임의 공통점: 이벤트루프

자바스크립트가 실행되는 환경(런타임)인 브라우저에서는 Web API(DOM, AJAX, Timeout 등)와 이벤트 루프, Callback Queue를 제공한다. 이 기능들이 자바스크립트로 하여금 비동기적으로 동작할 수 있도록 한다.

다시말해, 자바스크립트는 싱글스레드로 동기적으로 동작하지만, 자바스크립트가 실행되는 웹 브라우저에서 제공하는 이벤트루프 등으로 비동기적으로 동작할 수 있는 것이다.

자바스크립트 엔진은 혼자서 동작하지 않고, 웹브라우저, Node.js 등의 환경 내에서 실행된다. (이제는 더이상 이 두개의 환경에만 국한되지않고 모든 종류에 임베드되어 사용된다.) 이러한 환경들의 공통점은 모두 내장된 이벤트 루프라는 메카니즘이 있다는 것이다.

위의 이미지를 하나씩 살펴보도록 하자.

Web API

먼저 Web API란, 브라우저의 일부분으로 직접 접근할 수 없지만 API로 실행시킬 수 있는 스레드를 의미한다. 자바스크립트 실행환경이 Node.js라면, Web API가 아니라, C++ API가 될 것이다. (Node.js는 Javascript와 C++언어로 만들어졌기 때문)

Callback Queue

Web API가 실행되었다면, 콜백함수는 Callback Queue에 푸시되고, Call Stack이 빌때까지 대기한다.

Event Loop

이벤트 루프의 역할은 Call Stack과 Callback Queue를 모니터하는 것이다. Call Stack이 비었다면, Callback Queue에서 첫번째 이벤트를 가져와 Call Stack에 푸시할 것이다.


다시 위의 예시로 돌아가 생각해보자.

function first() {
  console.log('first');
}
function second() {
  console.log('second');
}
function third() {
  console.log('third');
}
first();
setTimeout(second, 1000);
third();

setTimeout 함수가 third 함수보다 먼저 실행되었지만, third 함수의 콘솔이 먼저 찍힌 이유는, setTimeout가 타이머를 생성하는 Web Api이므로 실행되기까지 시간이 걸려 Call Stack에서 빠져나와 Callback Queue에서 대기하고 있었기 때문이다.

  • first 함수가 Call Stack에 push되고, 실행(콘솔찍기)이 완료된 뒤 pop된다.
  • setTimeout 함수가 Call Stack에 push되고, 실행되어 브라우저는 Web Api의 일부로 타이머를 생성하고 setTimeout 함수는 Call Stack에서 pop된다.
    • Web API 내에 second 함수를 실행할 타이머는 남아있는 상태
  • third 함수가 Call Stack에 push되고, 실행(콘솔찍기)이 완료된 뒤 pop된다.
  • 적어도 1000ms 후 타이머가 끝나고, 콜백함수 second를 Callback Queue에 push한다.
  • 이벤트루프는 Call Stack이 비었는지 확인하고, 비었다면 second 함수를 Callback Queue에서 shift하고, Call Stack에 push한다.
  • second 함수는 실행된 뒤 Call Stack에서 pop된다.

reference

profile
어제보다 나은 오늘을 만드는 중
post-custom-banner

0개의 댓글