티스토리 뷰

반응형

이전글에서는 자바스크립트 엔진이 js를 어떻게 읽고 해석하는지에 다뤘습니다.

해당 글은구성요소와 이벤트루프가 어떻게 돌아가는지에 대한 글입니다.

 

? 자바스크립트 엔진이 또 다른 일을 해 ?

V8은 single thread으로 되어있는 실행 엔진이고 상당히 많은 일을 한다.

js코드를 해석, 실행도 하며 callStack을 다루며 memory 할당도 관리하고 GC도 하며 .. 등등을 담당한다.

뭐 엄청나게 많은 일을 하지만 여기선 이벤트루프 관련 글이기에 해당 부분만 다룬다.

 

자바스크립트 엔진의 구성요소

메모리들을 저장하는 영역인 Heap, 함수의 호출시점을 기억하는 Call Stack으로 이루어져있다.

 

Memory Heap

함수나 변수들을 저장하는 공간.

C의 경우 개발자가 할당한 메모리에 대해선 해제를 해주어야 leaks가 발생하지 않지만, JS나, Java와 같은 고-급언어들은 Garbage Collector가 알아서 해준다.

js는 mark and sweep의 방식을 쓴다고 알고있다. 그렇다고 만능은 아닌,,

더욱 자세한 메모리구조는 여기에서보자.

 

Call Stack ,Single Thread을 곁들인

V8은 thread가 하나로 이루어져있다고 했다. 한번에 하나의 일만 처리할 수 있는 것이다.

그러나 V8을 사용하는 브라우저나 node.js같은 런타임 환경이 single thread인 것은 아니다. 결국 js가 실행되는 본진은 싱글스레드긴 하지만,

무튼! 왜 Call Stack을 하나라고 할까?

프로그래밍언어의 함수 동작은 Stack 형식으로 진행된다.cpu관점에서의 함수호출을 보면 이해에 도움이 된다.

따라서 thread가 하나면 호출 스택이 1개라고 이해할 수 있다.

javascript가 single thread 언어라고 표현하는 건 js를 해석,실행하는 V8의 call stack이 하나이기 때문이다.

1_FB2VAMUAMVJpzxMBkGDR7Q

call stack이 동작하는 방식 - 이전 호출 순서를 기억해두었다가 종료시 pop하고 나머지 코드를 진행함

stack이 넘치면 stack overflow. - 재귀에서 탈출조건을 주어야하는 이유다

 

Single thread면 발생할 수 있는 문제가 하나있다.

다들 아시다시피

엄청난 시간이 소요되는 코드가 돌아갈 때 이후의 코드들은 수행되기까지 매우 오래 기다려야한다.

브라우저로 대상을 옮겨보면 클릭이벤트에 10초씩 걸린다하면 그 연산이 끝날 때까지 blocking되어 유저는 아무것도 못한다.

그렇지만 그런 경우는 없다. 어떻게 그럴까 ? 바로 비동기 작업을 통하기 때문이다

오래걸리는 일을 브라우저에게 던져놓고 나머지 동기적인 연산은 자바스크립트 엔진이 하는 것이다.

완벽한 분업!

이렇게 동작하기에 JS는 non-blocking되는 single thread기반의 비동기 처리를 하는 언어 라고 표현할 수 있겠다.

그렇다면 비동기작업은 어떻게 처리가 될까 ?

 

 

자바스크립트 엔진의 든든한 친구들

자바스크립트 엔진이 모든 작업을 도맡아하지 않는다.

비동기작업 담당인 브라우저라는 든든한 친구가 있기 때문이다.

스크린샷 2021-08-13 오후 11 38 00

이미지출처

사진에 보다시피 V8 이외 작업에 관여하는 요소들이 보인다.

저 브라우저의 요소들이 각각 어떤 방식으로 비동기작업을 도와주는지 살펴보자

 

 

Web API

브라우저에서 제공하는 작업들이다. 흔히 사용하는 setTimeout, Intersection Observer 등,, 엄청 많다.

여기에 있는 목록 전부 Web API다

Web API에서 직접적인 작업이 처리되고 이후, 전달된 call back이 Callback Queue로 들어가게된다.

setTimeout의 경우 특정 시간 이후 함수를 실행하는게 아니라 특정 시간 이후 callbackQueue로 callback을 옮기는 과정이다.

 

 

Callback Queue

Queue는 들어온 순서대로 나간다 - 게임 큐 잡히는거 생각하면 편함.같은거임

말그대로 callback이 들어온 순서대로 실행되기 위하여 callStack으로 나간다.

언제 어느시점에 나가느냐를 결정짓는 것은 Event Loop가 담당한다.

 

 

Event Loop

드디어 도달했다

Event loop의 역할은 Call Stack, Callback Queue를 감시하는 것이다.

Call Stack이 비어있을 때 Queue에서 callback을 Call Stack에 넣는다.

끝 ? -> 네 끝.

이 아니라 사실 Callback Queue는 하나가 아니다.

macro, micro task queue로 나뉜다

이 Queue는 Call Stack으로 들어갈 우선순위가 정해져있다.

이에 따라 Event Loop가 해당하는 callback을 call stack으로 넘겨주는 역할을 한다.

img

우선순위는 Micro > Macro 순이다.

gif에서 보듯이 Micro부터 call stack으로 올려주는 것을 확인할 수 있다.

이미지 출처_추가적인 내용도 포함!

 

 

그럼 각 queue에는 어떤 것이 들어갈까 ? 간단하게 알아보고 마무리 퀴즈를 통해 확실히 이해했는지 확인해보자!

Macro Task Queue

  • setTimeout, setInterval, setImmediate

Micro Task Queue

  • Promise callback, process.nextTick, queueMicrotask

 

 

🧑‍💻 정리

Event Loop에 대해 다시 정리해보자면,

single thread의 비동기처리를 위한 역할이고 이런 처리를 할 수 있는 구동방식은 다음과 같다.

Callback Queue(2가지)와 Call Stack을 감시하며 Call Stack이 비었을 때 먼저 Micro Queue의 가장 오래된(제일 앞) Callback을 꺼내어 Call Stack에 올리고, 이후 Macro의 Callback을 Call Stack에 올린다.

Promise가 set* 함수보다 먼저 Call Stack에 올라가게 되는 이유가 바로 Event Loop의 우선순위 때문이라는 것을 알고 있자.

 
 
막간 퀴즈 ! 이것 알면 모두 이해한 것!!!

정답은 댓글에 있으니 풀어보고 확인해주세요 !

 

console.log('Start');

setTimeout(()=> {
  console.log('setTimeout 0')
}, 0)

Promise.resolve()
    .then(() => {console.log('Promise 1')})
    .then(() => {console.log('Promise 2')})
    .then(() => {
  setTimeout(()=>{
    console.log('Promise 3 in setTimeout')
  }, 0)
})

console.log('End');
참고 링크
반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
농담곰의 고군분투 개발기