자바스크립트에서 이벤트는 DOM에 접근하여 어떤 사건을 가능하게 해줍니다.
자바스크립트의 특징 중 하나는 '단일 스레드'기반의 언어라는 점입니다.
단일 스레드라는 것은 동시에 하나의 작업만을 처리할 수 있습니다.
실제로 자바스크립트가 사용되는 환경을 보면 많은 작업이 동시에 처리되고 있습니다.
단일 스레드이지만 동시성을 가지는 것은 '이벤트 루프' 개념입니다.
브라우저 환경
구글의 V8을 비롯한 대부분의 자바스크립트 엔진은 크게 2개의 영역으로 나뉩니다.
작업이 요청되면 요청된 작업은 순차적으로 Call Stack에 쌓이게 되고 순차적으로 실행됩니다.
자바스크립트는 단 하나의 Call Stack을 사용하기 때문에 해당 task가 종료하기 전까지는 다른 어떤 task도 수행될 수 없습니다.
동적으로 생성된 객체 인스턴스가 할당되는 영역입니다.
동시성을 제공하기 위해 필요한 비동기 요청처리는 자바스크립트 엔진을 구동하는 환경 브라우저가 당담합니다.
비동기 처리 함수의 콜백 함수, 비동기식 이벤트 핸들러, Timer 함수의 콜백 함수가 보관되는 영역으로 이벤트 루프에 의해 특정 시점에 순차적으로 Call Stack으로 이동되어 실행됩니다.
태스크 큐(Task Queue)는 마이크로 태스크 큐(Event Queue) 와 매크로 태스크큐(Job Queue)로 나뉘어집니다.
API에 따라 마이크로 태스크 큐를 사용하거나, 매크로 태스크 큐를 사용합니다.
macrotasks: requestAnimationFrame, I/O, UI rendering, setTimeout, setInterval, setImmediate
microtasks: process.nextTick, Promises, queueMicrotask(f), MutationObserver
이벤트 루프를 통해 마이크로 태스크 큐가 실행된다면 마이크로 태스크들은 실행하면서 새로운 마이크로 태스크를 추가 할수 있습니다.
새롭게 추가된 마이크로 태스크도 큐가 빌 때까지 계속해서 실행됩니다.
하지만
이벤트 루프를 통해 매크로 큐가 실행된다면 매크로 태스크 큐만 실행 시킵니다.
매크로 태스크가 추가한 매크로 태스크는 다음 이벤트 루프가 실행 될 때까지 실행되지 않습니다.
Microtask Queue가 Macro Task Queue보다 먼저 실행됩니다.
Call Stack 내에서 현재 실행중인 task가 있는지 그리고 Event Queue에 task가 있는지 반복하여 확인합니다. 만약 Call Stack이 비어있다면 Event Queue 내의 task가 Call Stack으로 이동하고 실행됩니다.
const func1 = () => {
console.log('func1');
func2();
};
const func2 = () => {
console.log('func2');
setTimeout(() => {
console.log('Task Queue');
}, 0);
func3();
};
const func3 = () => {
console.log('func3');
};
func1();
/*
func1
func2
func3
Task Queue
*/
const a = () => {
return new Promise(resolve => {
resolve('new micro task');
}).then(res => console.log(res));
};
const b = () => {
return new Promise(resolve => {
resolve('new micro task2');
}).then(res => console.log(res));
};
const promise = new Promise(resolve => {
resolve('micro task');
}).then(res => console.log(res));
const func1 = () => {
setTimeout(function () {
console.log('macro task');
setTimeout(() => {
console.log('new macro task');
}, 0);
}, 0);
console.log('fun1');
func2();
};
const func2 = () => {
console.log('func2');
promise.then(a).then(b);
func3();
};
const func3 = () => {
console.log('func3');
};
func1();
/*
fun1
func2
func3
micro task
new micro task
new micro task2
macro task
new macro task
*/