@해당 글은 https://meetup.toast.com/posts/89 를 참고해 정리한 글임을 밝힙니다.
@이 글에서는 브라우저에서의 이벤트 루프만 다룹니다.
호출 스택(Call Stack)
js 가 실행할 함수 순서를 담아두는 스택
하나의 호출 스택을 사용하기 때문에
js 가 단일 호출 스택이라고 불리는 이유가 이것이다.
(ES6부터는 조금 달라졌다고 하지만 우선 이렇게 알아두자)
태스크 큐(Task Queue)
콜백함수들이 대기하는 큐
특정 조건이 만족되면 큐에 콜백함수들이 추가된다.
js 엔진의 바깥에 존재
이벤트 루프(Event Loop)
호출 스택이 비워질 때마다 테스크 큐에서 콜백함수를 꺼내와서 실행
js 엔진의 바깥에 존재
//내부적으로 이렇게 동작한다고 한다.
while(queue.waitForMessage()){
queue.processNextMessage();
}
브라우저 타이머(web API에 포함됨)
시간을 잰다.
$('.btn').click(function() { // (A)
try {
//서버의 응답을 요청하는 코드인듯
$.getJSON('/api/members', function (res) { // (B)
// 에러 발생 코드
});
} catch (e) {
console.log('Error : ' + e.message);
}
});
다음과 같은 코드는 try catch로 잡아낼 수 없음
브라우저는 서버의 응답을 받아 (B)를 테스크 큐에 추가하고, 호출 스택이 비어있으면 이벤트 루프는 (B)를 실행시킨다.
그런데 문제는 (B)는 이미 (A)의 컨텍스트 내에 있지 않다는 것. 그래서 (B)는 try catch에 영향을 받지 않는다.
0은 즉시를 의미하지 않는다.
$('.btn').click(function() {
showWaitingMessage(); // 비동기 함수, 로딩메세지를 보여주는 함수
longTakingProcess();
hideWaitingMessage();
showResult();
});
먼저 showWaittingMessage()의 렌더링 엔진이 렌더링 함수를 테스크 큐에 보낸다.
하지만 longTakingProcess() 와 이하 아래의 함수들이 호출 스택에 남아있어서 로딩 메세지 렌더링 함수는 showResult()가 실행되고 나서야 호출된다.
이런 문제를 막기위해 setTimeout(fn, 0)
을 사용한다.
아래는 해결 코드
$('.btn').click(function() {
showWaitingMessage(); // 비동기 함수, 로딩메세지를 보여주는 함수
setTimeout(function() {
longTakingProcess();
hideWaitingMessage();
showResult();
}, 0);
});
마이크로 테스크는 일반 테스크 보다 우선 순위가 높은 테스크라고 할 수 있다.
마이크로 테스크는 일반 테스크 큐에 저장되지 않고 마이크로 테스크 큐에 따로 저장된다.
아래 코드를 보자
setTimeout(function() { // (A)
console.log('A');
}, 0);
Promise.resolve().then(function() { // (B)
console.log('B');
}).then(function() { // (C)
console.log('C');
});
이 코드의 실행 순서는 B->C->A 이다.
Promise의 then() 메서드는 콜백을 마이크로 테스크 큐에 저장한다. 따라서
마이크로 테스크 큐 : B , C
테스크 큐 : A
이렇게 저장되어 B->C->A 순서로 실행된다는 것이다.