실행 컨텍스트에서 공부했듯이 함수의 실행 순서는 실행 컨텍스트 스택으로 관리하고, 함수가 실행되려면 함수 실행 컨텍스트가 실행 컨텍스트 스택에 푸시되어야 한다.
JS 엔진은 단 하나의 실행 컨텍스트 스택을 갖기 때문에, 함수를 실행할 수 있는 창구가 단 하나밖에 없다.
즉 JS 엔진은 한 번에 하나의 태스크만 실행할 수 있는 싱글 스레드 방식으로 동작한다. 싱글 스레드 방식은 한 번에 하나의 태스크만 실행할 수 있기 때문에 처리에 시간이 걸리는 태스크를 실행하는 경우 블로킹이 발생한다.
// sleep 함수는 delay 시간이 경과한 이후에 콜백 함수를 호출한다.
function sleep(func, delay) {
const delayUntil = Date.now() + delay;
while(Date.now() < delayUntil);
func();
}
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
// sleep 함수는 3000초 이상 실행된다.
sleep(foo, 3000);
// bar함수는 sleep 함수의 실행이 종료된 이루에 호출되므로 3초 이상 블로킹된다.
bar();
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
setTimeout(foo, 3000);
bar();
setTimeout
함수는 앞선 sleep
함수와 달리, setTimeout
함수 이후의 태스크를 블로킹하지 않고 곧바로 실행한다.setTimeout
, setInterval
, HTTP 요청, 이벤트 핸들러(커스텀 이벤트 디스패치, click, blur, focus 제외)는 비동기 처리 방식으로 동작한다.❓ JS는 싱글 스레드라고 했는데, 어떻게 비동기로 처리되는 것일까?
✅ 콜 스택
✅ 힙
비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리는 브라우저, 또는 Node.js가 담당한다.
🚨 태스크 큐(task queue/event queue/callstack queue)
setTimeout
이나 setInterval
같은 비동기 함수의 콜백 함수나 이벤트 핸들러가 일시적으로 보관되는 영역이다.🚨 이벤트 루프(event loor)
즉, 위쪽 코드에서 setTimeout
함수의 타이머가 완료되면 콜백 함수를 태스크 큐에 등록하는 처리는 자바스크립트 엔진이 아니라 브라우저가 실행한다. 자바스크립트 엔진은 단순히 태스크가 요청되면 콜 스택을 통해 요청된 작업을 순차적으로 실행할 뿐이다.
setTimeout의 콜백 함수는 태스크 큐에 푸시되어 대기하다가 콜 스택이 비게 되면, 콜 스택에 푸시되어 실행하게 된다.
아래 코드를 보자.
function func1() {
console.log('func1');
func2();
}
function func2() {
setTimeout(function() {
console.log('func2');
}, 0); // 0초로 설정
func3();
}
function func3() {
console.log('func3');
}
func1(); // ?
setTimeout
함수는 비동기 함수이기 때문에, 지연 시간
(delay)을 0초로 설정해도 func1 func2 func3
의 순서로 출력되지 않는다.
지연 시간은 0이지만 콜 스택이 비어야 호출되므로 약간의 시간차가 발생할 수 있기 때문이다. 즉, 정확히 지연 시간 후에 호출된다는 보장은 없다.
func1
함수가 호출되면 콜 스택에 쌓인다. func1
함수는 func2
함수를 호출해 func2
함수가 콜 스택에 쌓이고 setTimeout
함수가 호출된다.
브라우저는 타이머를 설정하고 타이머의 만료를 기다린다. 이 타이머가 만료되면 tick event가 발생한다. 이때 브라우저는 setTimout
의 콜백함수를 태스크 큐에 푸시한다.
즉 setTimeout
의 콜백 함수는 즉시 실행되지 않고 지정 대기 시간만큼 기다리다가, 시간이 끝나면 태스크 큐로 이동한 후 콜 스택이 비어졌을 때 콜 스택으로 이동되어 실행된다. 즉, 위의 예제의 결과는 func1 func3 func2
순으로 출력된다.
<출처 poiemaweb>
즉, JS엔진은 싱글 스레드이지만 브라우저는 멀티 스레드이다. JS와 브라우저는 협업하여 비동기 함수를 실행한다.
<모던 자바스크립트 deepdive와, 추가 탐구한 내용을 정리한 포스트입니다.>