자바스크립트는 싱글 스레드 기반의 언어이다.
자바스크립트는 비동기로 동작한다.
그럼 이벤트 루프란 무엇일까?
이벤트 루프를 알기 위해 자바스크립트를 해석하고 실행시키는 자바스크립트 엔진 에 대해 먼저 알아본다.
자바스크립트 엔진
자바스크립트 엔진은 자바스크립트로 작성한 코드를 해석하고 실행하는 인터프리터 이다. 웹 브라우저에 화면을 그리는 렌더링 엔진과는 다른 것이기 때문에 잘 구분해야 한다. (렌더링 엔진은 HTML과 CSS로 작성된 마크업 관련된 코드를 웹페이지에 렌더링 하는 역할을 함.)
자바스크립트 엔진은 주로 웹 브라우저에서 이용된다. node.js에서는 구글에서 개발한 V8과 같은 엔진이 이용된다. 이런 자바스크립트 엔진은 크게 Call Stack, Task Queue, Heap 3가지 영역으로 나뉜다. 여기에 추가적으로 이벤트 루프가 존재하며 Task Queue에 들어가는 task를 관리한다.
Call Stack (호출 스택)
자바스크립트는 단 하나의 호출 스택을 사용한다. 이 의미는 하나의 함수가 실행되면 이 함수가 끝날때 까지 다른 task가 수행될 수 없다는 의미이다. 요청이 들어올 때 마다 해당 요청을 순차적으로 호출 스택에 담아 처리한다. 메소드의 실행이 끝나면 pop된다.
function foo(a) {
var fooValue = 10;
return a + fooValue;
}
function bar(a) {
var barValue = 2;
return foo(a + barValue);
}
console.log(bar(6));
위 코드에서 bar 함수를 호출 했다. 그러므로 호출 스택에 push 되고, 그 안에는 barValue 라는 local variable이 생성된다. 그 후 bar 함수에서는 foo 함수를 호출하고 있다. 이 때 아직 bar 함수는 종료되지 않았으니 pop되지 않고 호출된 foo 함수가 호출 스택에 push 된다. foo 함수에서는 a + fooValue 값을 return 할 때 함수의 역할이 종료되므로 호출 스택에서 pop 된다. 그 후 다시 bar 함수에서 foo 함수로 부터 받은 리턴값을 return 하고 bar 함수도 종료되어 호출 스택에서 pop 된다.
Stack의 특성을 사용하여 task들을 수행하는 것을 알 수 있다.
Task Queue
자바스크립트 런타임 환경에서는 처리해야할 task들을 임시 저장하는 대기 큐가 존재한다. 이 대기 큐가 바로 Task Queue 이다. 호출 스택이 비어졌을 때 대기 큐에 들어온 순서대로 수행된다.
setTimeout(() => {
console.log('first');
}, 0);
console.log('second');
위 코드를 실행시키면 setTimeout에 0ms 를 주었기 때문에 first -> second 순서로 실행될것이라고 예상할 수 있지만 직접 실행해 보면 second -> first 순서로 콘솔창에 출력되는 것을 알 수 있다. 그 이유는 자바스크립트에서는 비동기로 호출되는 함수들은 호출 스택에 쌓이지 않고 Task Queue에 enqueue 된다. 자바스크립트에서는 이벤트에 의해 실행되는 함수들이 비동기로 실행된다. (e.g. 이벤트 핸들러) 자바스크립트 엔진이 아닌 Web API 영역에 따로 정의되어 있는 함수들은 비동기로 실행된다.
function foo() {
console.log('foo');
bar();
}
function bar() {
let timer = setTimeout(() => {
console.log('bar');
}, 0);
baz();
}
function baz() {
console.log('baz');
}
foo(); // ????
위 코드를 실행하면 어떤 순서로 콘솔에 찍힐까?
먼저 foo가 콘솔에 가장 먼저 찍힐 것이다. 그 후 bar가 호출되면서 setTimeout 함수가 실행되고 호출 스택에 들어간다음 바로 빠져나온다. 그리고 내부에 걸려있는 핸들러(익명함수)는 콜스택에 들어가서 바로 실행되지 않고, event queue 영역으로 들어간다. 그리고 baz 함수가 호출 스택으로 들어가면서 baz가 콘솔에 찍히고 작업을 모두 마쳤기 때문에 호출 스택에서 pop 된다.
이어서 bar 함수와 foo 함수또한 호출 스택에서 pop 된다. 이 때 이벤트 루프의 콜스택이 비어있게 된다. 이 시점에 queue의 head에서 하나의 이벤트를 가져와 호출 스택으로 넣는다. 이 때 들어가는 이벤트가 setTimeout에 있던 익명함수 이다.
정리하자면, baz 함수 호출 스택에서 pop -> bar 함수 호출 스택에서 pop -> foo 함수 호출 스택에서 pop 되고 나서 이벤트 루프에 의해 하나의 이벤트가 dequeue 되고 다음 호출 스택으로 들어가서 실행된다. 그렇기 때문에 이벤트에 걸려있는 핸들러는 절대 먼저 실행될 수 없다. (foo -> baz -> bar 순서로 콘솔에 찍힌다.)
이벤트 루프는 현재 실행 중인 task가 없는지, task queue에 task가 있는지 반복적으로 확인한다. queue에 처리해야할 이벤트가 있다면 해당 하는 이벤트를 처리하고, 다시 queue로 돌아와 새로운 이벤트가 존재하는지 파악한다.
JBee님 블로그를 참조하여 작성했습니다.
[JS] JavaScript의 Event Loop
[JS] Javascript 작동 원리에 대해서, Event Loop Javscript를 공부하다 보면 이런 말을 종종 듣는다. 싱글스레드 기반으로 동작하는 자바스크립트 이벤트 루프를 기반으로 하는 싱글 스레드 Node.js 이런
asfirstalways.tistory.com
'Javascript' 카테고리의 다른 글
[클린코드 자바스크립트] 호이스팅 주의하기 (0) | 2021.11.11 |
---|---|
[클린코드 자바스크립트] 임시변수 제거하기 (0) | 2021.11.11 |
타입스크립트 - Utility Type & Mapped Type (0) | 2021.04.13 |
타입스크립트 - 타입 가드 (0) | 2021.04.12 |
타입스크립트 - 타입 추론 & 단언 (0) | 2021.04.12 |
댓글