이벤트 루프

fromzoo·2020년 12월 10일
0

'어떻게 자바스크립트는 싱글스레드이면서 비동기인걸까?'에 대한 짧은 답은

자바스크립트 언어는 싱글스레드이고 비동기 행위들은 엄밀히 말하면 자바스크립트 언어 그 자체의 일부는 아니다.

오히려 비동기 행위는 브라우저 내부(혹은 프로그래밍 환경)에 존재하는 자바스크립트 언어의 핵심(core) 단위 상위에 만들어져 있다.

그리고 브라우저 API를 통해 접근한다.

기본 아키텍쳐

  • 힙영역
    메모리 할당이 일어나는 곳
    (우리가 프로그램에 선언한 변수, 함수 등이 담겨져 있다)
  • 스택영역
    자바스크립트 코드 실행을 위해 제공된 싱글 스레드를 나타낸다. 함수 호출은 frame의 스택을 구성한다.....

정리하자면
코드가 실행될때 쌓이는 곳.
stack형태로 쌓임
stack 은? 자료 구조중 하나, 선입후출의 룰을 따른다.

  • 브라우저 또는 웹 API
    브라우저와 웹API는 웹 브라우저에 구성되며 브라우저로 혹은 주변 컴퓨터 환경으로부터 데이터를 노출시킬 수 있고 이것을 통해 유용하지만 복잡한 것들도 할 수 있다.

사실 이것들은 자바스크립트 언어 그 자체는 아니고 오히려 자바스크립트 언어 코어 단의 상위에 만들어져 우리가 자바스크립트 코드 사용시에 추가적인 초능력을 제공한다.

예를들면 Geolocation API는 지역 데이터를 가져오기 위한 간단한 자바스크립트 구조를 제공한다. 그래서 구글맵에 위치를 표기할 수 있다.

백그라운드에서 브라우저는 디바이스의 GPS하드웨어와 통신하기 위해 (또는 위치데이터를 결정할 수 있는 무엇이든) 사실 C++ 같은 복잡한 로우레벨 코드를 사용하는 중이다.

위치 데이터를 불러오고 코드에서 사용할 수 있도록 브라우저 환경에서 이것을 반환한다. 하지만 이런 복잡성은 다시 API에 의해 추상화되어있다.

정리하자면 그림에서 Web API는 JS Engine의 밖에 그려져 있다. 즉, 자바스크립트 엔진이 아니다. Web API는 브라우저 에게 제공하는 API로, DOM, Ajax, Timeout등이 있다.

Call Stack에서 실행된 비동기 함수는 Web API를 호출하고 Web API는 콜백함수를 Callback Queue에 밀어넣는다.

코드 1: 사고방식 깨우기

function main() {
	console.log('A'); 

	setTimeout(
		function display() {
			console.log('B')
		}
	,0);

	console.log('C')
}

main()
// Output
// A
// C
// B

코드 실행 중 내부에서 일어나는 일들

  1. 메인 함수에 대한 호출이 먼저 프레임으로 stack에 push된다.

그 후에 브라우저가 메인함수 내부의 첫번째 statement(console.log('A'))를 stack에 넣는다. 이 statement가 실행되고 완료되자마자 해당 프레임은 스택에서 pop된다. 알파벳 A가 콘솔에 표기된다.

  1. 다음 statement (콜백 display()과 함께 0ms의 딜레이를 가진 setTimeout())가 콜스택으로 push되고 실행된다.

setTimeout함수는 제공된 콜백을 딜레이하기 위해 브라우저 API를 사용한다. 일단 타이머를 돌리기 위해 콜백이 브라우저로 넘어가면 setTimeout()을 가진 프레임은 pop된다.

  1. 브라우저에서 display() 실행을 위한 타이머가 돌아가는 도중에 console.log('C')가 콜스택에 push된다.

이러한 경우에는 제공된 딜레이는 0ms였기 때문에 콜백은 브라우저가 콜백을 받자마자 메세지 큐에 바로 추가된다. (이상적인 경우)

  1. 메인 함수에서 마지막 statement의 실행후에 main()프레임은 콜스택 밖으로 pop 된다. 그러면 콜스택은 빈 상태가 된다 브라우저가 어떤 메세지를 큐에서 콜스택으로 push하기 위해서는 먼저 콜스택을 반드시 비워야 한다.

이게 setTimeout의 딜레이가 0초였음에도 불구하고, display로의 콜백이 콜스택에 존재했던 모든 프레임이 실행될때까지 기다려야 했던 이유이다.

  1. 이제야 콜백 display()이 콜스택에 푸시되고 실행된다.

그다음 알파벳C가 콘솔에 나타난다.

이게 자바스크립트 이벤트 루프이다.

그래서 setTimeout(function,delayTime)에 들어가는 delay 파라미터는 어떤 함수가 실행된 뒤의 정확한 시간 딜레이를 말하는 것이 아니다. delay파라미터는 함수가 실행됐을때의 어떤 지점 이후의 최소 대기시간을 의미한다.

코드 2: 더 깊은 이해

function main() {
	console.log('A');
	
	setTimeout(
		function exec() {console.log('B');}
		,0);
		
	runWhileLoopForNseconds(3);
	
	console.log('C');
}

main();

function runWhileLoopForNseconds(sec) {
	let start = Date.now(), now = start;

	while (now - start < (sec * 1000) ) {
		now = Date.now();
	}
}


// Output
// A
// C
// B

  • 함수 runWhileLoopForNSeconds()는 정확히 이름과 같은 일을 한다.

함수가 호출된 시간에서 경과된 시간을 계속 측정하여 경과된 시간이 함수의 인자로 받은 시간과 일치하는지 계속해서 검증한다.

기억해야할 메인 포인트는 while반복문이 콜스택에서 상주하면서 브라우저 API를 사용하지 못하게 하는 blocking statement라는 것이다.

이 함수는 실행이 끝날때까지 뒤에 오는 모든 statements들이 실행되지 못하게 막는다.

  • 그래서 위의 코드에서 비록 setTimeout은 0초의 딜레이를 가지고 while 반복문은 3초간 실행되더라도 exec() 콜백은 메세지 큐에 갇혀있다.

while 반복문은 3초가 지날때까지 콜스택 위에서 계속 실행된다. ( js 엔진은 싱글스레드이기 때문)

3초가 지나 콜스택이 비게 되는 순간에 exec() 콜백은 콜스택으로 들어와서 실행된다.

  • setTimeout()의 딜레이 인자는 실행이 시작되는 타이밍을 보장해주는 것이 아니다.

최소한 얼마정도 있다가 실행되라 정도의 의미로 보는 것이 옳다.

🔍 출처 블로그
자바스크립트 개발자라면 알아야 할 33가지 개념 #9 자바스크립트 이벤트 루프

profile
프론트엔드 주니어 개발자 🚀

0개의 댓글