이벤트 루프 (Event Loop)

yoo chang heon·2022년 3월 23일
0

JavaScript

목록 보기
2/9

자바스크립트는 Single Thread 언어이다. -> 콜스택이 하나만 존재한다. -> 한번에 하나의 일만 처리

비동기?

하지만 분명히 자바스크립트로 이뤄진 웹은 동시에 일을 처리하는 것으로 보인다. \rightarrow 이벤트 루프(Event Loop). 이벤트 루프는 자바스크립트 엔진에 있는 것이 아니라 브라우저나 Node.js에서 처리한다.

자바스크립트 엔진

자바스크립트 엔진(JavaScript engine)은 자바스크립트 코드를 실행하는 프로그램 또는 인터프리터이다. 대표적인 예로 Google V8 엔진이 있다. (Chrome, Node.js에서 사용)

자바스크립트 엔진 동작

  1. HTML 파서가 HTML 소스에서 <Script> 태그를 만난다.
  2. 자바스크립트 소스 코드가 파싱된다. \rightarrow 노드를 만들어낸다.
  3. 파싱된 자료를 이용해 AST를 생성(추상구문트리)
  4. AST를 인터프리터를 통해 바이트 코드(가상머신이 이해할 수 있는)로 변경
  5. 자주 쓰는 구문을 최적화하기 위해 컴파일 \rightarrow 최적화 코드 만듬
  6. AST로 바이트코드 \rightarrow 자주 쓰는 구문을 최적화 코드로 만듬, 이 둘을 알맞게 CPU에 전달

런타임

출처

콜 스택

실행되는 순서를 기억해주는 자료구조이다. 함수를 실행하려면 스택에 함수를 집어넣고, 다 실행된다면 스택에서 제거한다.

콜백 큐

자바스크립트 런타임 환경에서 처리해야하는 명령어를 임시로 저장하는 대기큐로 비동기적으로 실행된 콜백함수가 보관되는 영역이다.

콜백 큐는 하나인 것 처럼 보이지만 사실 여러개로 구성되어있다.

  • Task Queue(Event Queue): setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI 렌더링 등이 이곳에 콜백함수를 넣는다.
  • Microtask Queue: process, nextTick, Promise, Object.observe, MutationObserver
  • Animation Frames: requestAnimationFrame API(브라우저에게 수행하기를 원하는 애니메이션을 알리고 다음 리페인트가 진행되기 전에 해당 애니메이션을 업데이트하는 함수를 호출)

콜백 큐 우선순위

Microtask Queue < Animation Frames < Task Queue

실행 예시

예시 코드를 보자

console.log('hi');

setTimeout(function(){
	console.log('there');
},5000);

console.log('JSConfEU');

이를 실행하면 hi \rightarrow there \rightarrow (5초 후에) JSConfEU 가 출력이 된다.
먼저 콜스택에 console.log('hi') 가 들어가고 setTimeout 다음 console.log('JSConfEU') 순서로 들어간다.
여기서 setTimeout은 브라우저에서 제공하는 API이다. (자바스크립트가 실행되는 런타임 환경에 존재하는 별도의 API) 브라우저에서 setTimeout의 5초를 세준다. 이건 setTimeout의 호출 자체는 완료되었다는 의미고 우리는 스택에서 함수를 지울 수 있다. 그럼 마지막 남은 console.log('JSConfEU')이 실행되고 끝난다.
그렇게 스택은 비어있게 되고, webAPI에 setTimeout만 남았다. webAPI에서 타이머를 실행 완료하면 테스크 큐에 push한다. 이벤트 루프는 아주 단순한 일을 한다. 역할은 콜 스택과 테스크 큐를 주시한다. 콜 스택이 비었다면 테스크 큐의 첫번째 콜백을 스택에 쌓아 실행할 수 있게 해준다. 스택에서는 함수를 실행하고 제거한다.

추가)

가끔 문제가 생겼을때 setTimeout 0으로 해결되는 경우가 있다. 이는 아무런 의미가 없는 행위인 것 같지만 스택이 비어있을 때까지 기다리게 하기 위한 작업이다. (이벤트 루프는 스택이 비어있을때만 넣을 수 있기 때문)
하지만 setTimeout 이 여러 개 쌓이게 되면 테스크 큐에서 쌓여서 기다리게 되기 때문에 정확한 시간을 보장할 수는 없다. 대신 딜레이되는 최소한의 시간만을 지정할 수 있다.
AJAX요청 또한 Javascript 런타임이 아니라 브라우저 webAPI에서 실행된다. 여기서 실행할 때는 JS엔진의 콜 스택에서 다른 코드가 정상 작동할 수 있다.

[1,2,3,4].forEach(function(i){ //---1번
	console.log(i);
});

function asyncForEach(array,cb){ //----2번
	array.forEach(function(){
    	setTimeout(cb,0);
    })
}

asyncForEach([1,2,3,4], function(i){ 
	console.log(i);
})

1번은 자신의 자체적 스택에서 실행
2번은 배열과 콜백을 받아 각요소에서 setTimeout을 0으로 실행

실험

그러면 콘솔을 찍다가 중간에 timer나 api를 부르고 콘솔을 무한하게 찍으면 api는 실행될수 없는걸까??

for (let i = 0; i < 100000; i++) {
  console.log("안녕?");
  if (i === 5000) {
    setTimeout(function () {
      console.log("나도 말 좀하자");
    }, 1000);
  }
}

간단하게 코드를 짜서 실험해봤다.

5000번째가 훌쩍 넘어갔음에도 말을 하지 못한다.


십만번 인사듣고 한마디 겨우 한마디하는 모습.

결론: 무한하게 돌리다가 컴퓨터가 터져서 확인은 못했지만 마지막까지 밀리는 것보니 실행 못할 것 같다.

참조영상

0개의 댓글