April 27, 2021
자바스크립트 타이머(setTimeout, setInterval)는 지정한 시간에 콜백 함수가 실행되는 것을 보장하지 못한다.
그 이유는, 싱글 스레드와 아주 밀접한 관련이 있다.
자바스크립트는 싱글 스레드 기반의 언어이다.
스레드가 하나라는 말은 동시에 하나의 작업 만을 처리할 수 있다는 것인데, 왜 많은 작업이 동시에 처리되고 있는 것처럼 보이는 것일까? 이벤트 루프 때문이다.
자바스크립트는 이벤트 루프를 이용해서 비동기 방식으로 동시성을 지원한다.
function func1() {
func2();
console.log(1);
}
function func2() {
console.log(2);
}
function func3() {
console.log(3);
}
setTimeout(func3, 0);
func1();
/*
출력 결과 :
2
1
3
*/
setTimeout
함수를 통해 넘긴 func3
함수는 어떻게 func1
함수가 끝나자마자 실행될 수 있을까? 태스크 큐와 이벤트 루프 때문이다.
태스크 큐는 콜백 함수들이 대기하는 큐(Queue) 형태이고, 이벤트 루프는 호출 스택이 비워질 때마다 태스크 큐에서 콜백 함수를 꺼내와서 실행하는 역할을 해준다.
func1
이 실행을 마치고 콜 스택이 비워지면 그 때 이벤트 루프가 태스크 큐에 대기 중인 첫 번째 태스크인 func3
를 실행해서 호출 스택에 추가하는 것이다.
이벤트 루프는 현재 콜 스택이 비워졌는 지와 태스크 큐에 태스크가 있는지를 반복적으로 확인한다.
setTimeout 함수는 콜백 함수를 바로 실행하지 않고 콜 스택이 아닌 태스크 큐에 추가한다.
즉, setTimeout 으로 0초 뒤에 func3
을 실행하는 것이 아니라 0초 뒤에 태스크 큐에 추가하는 것이기 때문에 콜 스택이 비워지고 나서 func3
을 실행하는 것이다.
setTimeout(function myCallback() {
console.log('1.5초 타이머 종료');
}, 1500);
for (let i = 0; i < 3; i++) {
doSomething(); // 가정: 매번 1초가 걸리는 일
console.log(i);
}
console.log('3초 걸리는 for문 종료');
/*
출력 결과 :
(약 1초 뒤에) 0
(약 2초 뒤에) 1
(약 3초 뒤에) 2
3초 걸리는 for문 종료
1.5초 타이머 종료
*/
doSomething
은 매번 1초가 걸리는 일이라고 가정하자.myCallback
의 console.log('1.5초 타이머 종료')
는 1.5초 뒤가 아닌 약 3초 뒤에 출력이 된다. (싱글 스레드이기 때문)setTimeout(myCallback, 1500);
은 1.5초 뒤에 실행될 것이라기 보다는 1.5초 이후에 태스크 큐에 추가된다는 것을 의미한다.myCallback
은 실제로 실행되기 전에 다른 이벤트들이 끝날 때 까지 기다려야 할 수 있다.