자바스크립트는 싱글 스레드 기반의 언어이고, 자바스크립트 엔진은 하나의 호출 스택만을 사용한다. 이는 요청이 동기적으로 처리되어, 한 번에 한 가지 일만 처리할 수 있음을 의미한다.
만약, 네트워크 요청과 같은 비동기 함수가 동기적으로 이루어지는 함수로 만들어졌다면, 어떤 일이 일어날까?
네트워크 요청이 다른 서버로 보내지고, 컴퓨터는 응답 받기를 기다리며 느려질 것이다. 그 사이에 클릭이나, 다른 요소가 렌더링이 되어져야 하는게 있더라도, 스택은 네트워크 요청 함수에 블락킹 되어있으므로, 아무 일도 일어나지 않게 된다.
이러한 문제는 비동기 콜백을 사용함으로써 해결된다. 비동기 요청은 자바스크립트 엔진을 구동하는 환경인 브라우저나 Node.js에서 처리된다.
Task queues are sets, not queues, because step one of the event loop processing model grabs the first runnable task from the chosen queue, instead of dequeuing the first task.
Node.js 는 비동기 IO를 지원하기 위해 libuv
라이브러리를 사용한다. 이 libuv가 이벤트 루프를 제공한다.
브라우저와 Node.js에서 공통으로 제공하는 것이 이벤트 루프
이다. 자바스크립트는 싱글 스레드 기반의 언어지만, 자바스크립트가 구동되는 환경(Node.js, 브라우저)은 여러 스레드가 사용된다. 여러 스레드가 사용되는 구동 환경이 자바스크립트 엔진과 연동하기 위해 사용되는 장치가 '이벤트 루프' 이다.
웹 사이트나 애플리케이션의 코드는 메인 스레드에서 실행되며, 같은 이벤트 루프를 공유한다.
메인 스레드는 사이트의 (1)코드를 실행시킬 뿐만 아니라, (2)이벤트들을 받고 실행시키거나, (3)웹 컨텐츠를 렌더링하거나 페인팅 하는 일들을 한다.
즉, 이벤트 루프는 스레드 안에 있는 코드들을 스케쥴링하고 실행시키는 역할을 담당한다.
이벤트 루프는 세 가지 종류가 있다.
마이크로 태스크들은 실행하면서 새로운 마이크로 태스크를 큐에 추가할 수도 있다. 새롭게 추가된 마이크로 태스크도 큐가 빌 때까지 계속해서 실행된다.
반대로, 이벤트 루프는 매크로 태스크 큐에 있는 것을 실행시키기 시작할 때 있는 매크로 태스크만 실행시킨다. 매크로 태스크가 추가한 매크로 태스크는 다음 이벤트 루프가 실행될 때까지 실행되지 않는다.
태스크 큐는 구체적으로 마이크로 태스크큐(Event Queue) 와 매크로 태스크큐(Job Queue) 로 나뉘어진다.
API에 따라 마이크로 태스크 큐를 사용하거나, 매크로 태스크 큐를 사용한다.
macrotasks: requestAnimationFrame, I/O, UI rendering, setTimeout, setInterval, setImmediate
microtasks: process.nextTick, Promises, queueMicrotask(f), MutationObserver
이벤트 루프는 다음을 반복한다.
다음 코드를 보고 결과를 예상해보자.
console.log('script start'); // A
setTimeout(function () { // B
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(function () { // C
console.log('promise1');
})
.then(function () { // D
console.log('promise2');
});
console.log('script end'); // E
script start
script end
promise1
promise2
setTimeout
코드는 위에서부터 차례로 실행된다. 이곳에서 실행되는 과정을 step-by-step으로 확인할 수 있다.
1. 콜 스택에는 전역 실행 객체가 있고, '스크립트 실행'이라는 태스크가 매크로 태스크 큐에 들어있다.
2. 이벤트 루프는 매크로 태스크 큐에 있는 '스크립트 실행' 태스크를 실행한다.
3. A에 도달하면, 'script start'가 출력된다.
4. B에 도달하면, setTimeout web api가 타이머를 실행시키고, 타이머가 종료되면 콜백 함수가 매크로 태스크 큐에 들어간다.
5. C에 도달하면, 콜백 함수가 마이크로 태스크 큐에 들어간다.
6. E에 도달하면, 'script end'가 출력된다.
7. 콜 스택이 비었으므로, 이벤트 루프는 마이크로 태스크 큐에 있는 프라미스 콜백 함수를 실행시킨다.
8. 'promise 1'이 출력된다.
9. Promise.then 메서드는 D 콜백 함수를 마이크로 태스크 큐에 등록한다.
10. 이벤트 루프는 다음 마이크로 태스크인 D 콜백 함수가 실행시킨다.
11. 'promise 2'가 출력된다.
12. 렌더링할 것이 있으면, 브라우저는 렌더링을 한다.
13. 매크로 태스크 큐에 있는 setTimeout 콜백함수를 실행시킨다.
14. 'setTimeout'이 출력된다.
내용 정말 좋군요.