JavaSript Runtime (ex. Nodejs) has single thread!!
함수가 load-unload 되는 공간.
LIFO 구조를 갖는 이름 그대로의 스택형 메모리다.
함수단위로 실행을 종료할 때, 해당 함수는 unload(pop)된다.
function, object, variable 모든 것을 담는 unstructured momory 공간
garbage collector 가 존재한다.
실행할 함수 목록을 갖고있는 메시지 큐
콜스택에 충분한 공간이 있으면 메시지 안에 담긴 함수 리스트를 콜스택에 로드하여 실행시킨다.
이 때, 이벤트에 대한 '콜백'함수가 제공되면 외부 비동기 이벤트의 '응답' 메시지 (응답되는 콜백함수 자체)가 큐에 또 쌓인다.
ex) setTimeout
, fetch
, I/O tasks
Promise
객체나 async/await
함수의 콜백함수가 큐에 들어간다. Callback Queue가 아닌 Job Queue에 들어간다.
Callback Queue 보다 높은 우선순위를 가진다.
ex) Promise
, process.nextTick
💡 Event loop 은 Job Queue 가 비워져야 Callback Queue 를 검사한다.
(Job Queue > Callback Queue)
백그라운드에서 시간이 흐를 때, event loop 은 non-block 상태로 계속 돌고, I/O 작업시 내부적으로 Thread-Pool 의 thread 를 사용한다.
이 때 Thread-Pool 은 libuv 라는 커널 비동기I/O 를 지원하는 라이브러리에 존재한다.
📝 기본적으로 libuv 는 4개의 쓰레드가 존재한다.
built in funciton 으로 setTimeout
, fetch
같은 메소드가 있다.
setTimeout
은 호출되자마자 콜스택에서 팝되고 백그라운드에서 시간이 흐른다. 지정된 시간이 지나면 Queue 로 등록된 콜백함수를 보낸다.
콜스택과 큐를 모니터링한다.
콜스택이 비어있을 때 큐의 첫번째 메시지를 콜스택으로 넣는다.
// blocking in callstack
function waitSecond(second: number) {
const nowSecond = Math.floor(Date.now() / 1000);
// 시간이 3초 흐를 때 까지
while (Math.floor(Date.now() / 1000) < nowSecond + second) {
// @NOTHING TO DO
}
return;
}
function main() {
// push call stack console.log
console.log(1);
// insert message to queue
// Event loop monitoring call stack and queue, if stack is empty, push first message of queue to call stack
// to speak correctly, callback function 'console2' is pushed to queue.
setTimeout(function console2(){
console.log(2);
}, 0)
// push call stack waitSecond (setTimeout is not in call stack, it's in queue until stack is empty)
waitSecond(3);
// console '3' and return main function (POP)
console.log(3);
// now stack is empty state => Event Loop check stack is empty => push 'setTimeout' into Call stack
return;
}
1
3
2
main
함수를 콜스택에 넣는다.console.log(1)
콜스택에 넣고 실행시킨 후 pop 한다.setTimeout
을 콜스택에 넣고console(2)
백그라운드에서 타이머를 가동시킨다. (0ms라 바로 타이머는 끝나자마자 콜백 함수를 queue 에 넣는다.)setTimeout
을 콜스택에서 pop 한다. // 3에서 타이머가 만료되지 않아도 곧바로 콜스택에서 제외된다.waitSecond
를 콜스택에 넣고 실행한다.waitSecond
를 pop 한다.console.log(3)
을 콜스택에 넣고 실행시킨 후 pop한다.setTimeout
에서 queue에 넣어둔 console2
메소드를 꺼내서 콜스택에 넣는다.console2
메소드를 콜스택에 넣어 실행시키고 리턴한다.setTimeout에서 타이머를 0ms 로설정해도 '2'가 마지막에 찍히는 원인을 알게되었다.
사실상 비동기함수는 모두 '콜백'함수를 Queue 에 넣는 것과 다를바 없다.