자바스크립트 - Call Stack, Event Loop, Tasks & more

코드위의승부사·2020년 2월 14일

JavaScript

목록 보기
8/8

자바스크립트의 특징
자바스크립트는 single threaded, single concurrent language이다.
이 말은 한 번에 한가지 일, 한 번에 한 조각의 코드를 다룰 수 있다는 의미다.

V8 엔진의 구성

Javasript Concurrency Model(V8엔진 내부에서 작동하는)은 single call stack, heap, queue 로 이루어졌다.

Call Stack

함수 호출이 기록되는 자료 구조이다.
만약 함수를 실행하게 되면, 무언가 스택에 push되고 함수로 부터 리턴 받을때 스택의 가장 윗부분이 pop된다.

위의 파일을 실행하면, 모든 실행이 시작되는 곳인 main 함수를 첫번째로 찾을수 있다.
그 위에 스택에 push된 console.log(bar(6))부터 시작한다.
다음으로 함수 bar가 인자와 함께 올라가게 되고 다시 foo 함수가 스택의 위에 push되고 즉시 스택 밖으로 pop된다.
bar와 비슷하게 마지막에 있는 console이 pop되고 결과가 나오게 된다.
이 모든 것들이 한순간에 일어난다.

아래와 같은 빨간색의 긴 에러 stack trace를 콘솔에서 가끔씩 볼 수 있다.
현재 콜스택의 상태를 나타내주고 함수안에서 스택과 마찬가지로 위에서 아래의 이동이 실패한다.

때로는 함수를 반복해서 호출하는 infinite loop에 직면하게 된다.
스택의 사이즈는 16,000 프레임으로 제한되있는데 그보다 많은 경우 Max Stack Error Reached로 throw한다.

Heap

객체들은 대부분은 메모리의 구조화되지 않은 영역과 같은 heap에 위치한다.
변수나 객체들에 대한 메모리 할당이 이곳에서 일어난다.

Queue

자바스크립트 런타임은 처리할 message 목록, 실행 관련 콜백 함수가 포함된 message queue를 포함한다.
스택이 충분한 용량이 있을 때, 메시지를 큐에서 꺼내서 관련 함수를 호출하여 진행시킨다(이렇게 초기의 스택 프레임을 만든다)
스택이 다시 비어지면 메시지 처리가 종료된다.
기본 용어로, 이런 메시지들이 외부 비동기 이벤트들에 대한 응답, 콜백 함수가 제공된 경우에 대해 큐이다.(마우스 클릭됨, HTTP request의 응답 수신)
예를 들어, 사용자가 버튼을 클릭하고 콜백 함수가 없다면 어떤 message도 enqueue되지 않았을 것이다.

Event Loop

JS code의 성능에 대해 얘기할 때, 스택안의 함수를 느리거나 빠르게 만들 수 있다.
console.log()는 빠르지만 많은 수의 for나 while은 느려질 것이고, 스택이 계속 사용되거나 블락 될것이다.
여기서 사용되는 용어가 blocking script이다.

네트워크 요청은 느려질 수 있지만 서버의 요청은 비동기 함수인 AJAX를 통해 수행된다.
만약 네트워크 요청이 동기적으로 처리 된다면 어떤 일이 생길까?
네트워크 요청이 다른 곳에 위치한 서버로 전송되고 그 서버의 응답은 느려질 수 있다. 그 동안 내가 어떤 CTA 버튼을 클릭하거나 다른 렌더링이 완료되어야 한다면 스택이 block되어서 아무 일도 일어나지 않을것이다.
어떻게 이 상황을 해결할 수 있을까?

Concurrency in JS - One Thing at a Time, except not Really, Async Callbacks

가장 쉬운 방법은 비동기 콜백을 활용하는 것이다.
일 부분의 코드를 실행하고 그 이후에 실행되는 콜백을 준다.
AJAX요청에서 사용하는 $get, 과 같은 비동기 콜백을 볼것이다.
노드가 모든 비동기 함수 실행에 대해 관련이 있다.
모든 비동기 콜백이 즉시 실행되지 않고 약간의 시간 후에 실행될 것이다. console.log나 연산작업과 다르게 즉시 스택안으로 push되지 않을 것이다
그러면 그들을 어디로 가고 어디서 처리되는 것일까?

위의 네트워크 요청하는 코드를 보면
1. 요청함수가 실행되면, onreadystatechange 안에 있는 익명의 함수가 전달되고 응답으로 이용가능할 때에 콜백이 이벤트로서 실행된다.

2.console에 'Script call done!'이 즉시 출력된다.

3 응답이 오면 콜백이 실행되고 결과물이 콘솔로 나타나게된다.

응답으로부터의 호출의 디커플링을 통해 JavaScript 실행 시간이 다른 작업슬 수행하는 동안 비동기 작업이 완료되고 콜백이 실행될때까지 기다릴 수 있다.

2번 - 브라우저API가 시작되고 API를 호출한다.
DOM 이벤트, http 요청, setTimeout등 비동기 이벤트들을 처리하기 위해 C++ 로 구현된 브라우져에 의해서 스레드가 만들어진다.

Browser Web APIs- threads created by browser implemented in C++ to handle async events like DOM events, http request, setTimeout, etc.

이제 이 WebAPIs는 그들 스스로 execution code를 스택에 넣을 수 없다. 만약 그랬다면 코드 중간에 임의로 나타날 것이다.
위에서 얘기한 message callback queue는 방법을 보여준다.

3번 - WebAPI중 하나가 실행이 완료 되었을때 그 콜백을 queue에 push한다.
이제 이벤트 루프는 queue에 있는 콜백들의 실행하고, 스택이 비었다면 콜백을 푸쉬 한다.

4번 - 이벤트 루프의 기본적인 일은 stack(현재 실행중인 태스크)과 task queue를 살펴보고 스택이 비었다면 queue의 첫번째 것을 스택으로 push하는 것이다.(한번에 하나씩 Call Stack으로 호출되어 처리)
각 message나 콜백은 다른 message가 진행되기전에 완벽하게 진행될것이다.

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

웹브라우져 안에서 messages 이벤트가 발생하면 언제든 추가되고 이벤트 리스너가 추가될 수 있다. 만약 리스너가 없다면 이벤트는 사라질 것이다.
엘레멘트를 클릭하면 메시지가 추가되는 이벤트핸들러나 다른 어떤 이벤트들 같이
이런 콜백함수의 호출이 call stack의 초기프레임 역할을 하며 자바스크립트가 single-threaded이기 때문에 스택의 모든 호출이 반환될때까지 message polling이나 진행이 중지된다.
동기적인 함수 호출은 새로운 호출프레임을 스택에 추가한다.

Reference

profile
함께 성장하는 개발자가 되고 싶습니다.

0개의 댓글