자바스크립트의 특징 중 하나는 싱글 스레드로 동작한다는 것인데, 그럼에도 불구하고 많은 태스크가 동시에 처리되는 것을 알 수 있음. 자바스크립트의 동시성을 지원하는 것이 바로 이벤트 루프임
싱글 스레드 방식으로 동작하는 것은 브라우저(nodejs)가 아니라 내장된 자바스크립트 엔진이라는 것을 주의하기! 모든 자바스크립트 코드가 자바스크립트 엔진에서 싱글 스레드 방식으로 동작하면 자바스크립트는 비동기로 동작할 수 없음 ( 즉, 자바스크립트 엔진은 싱글 스레드 브라우저(nodejs)는 멀티 스레드로 동작)
자바스크립트 엔진

- 자바스크립트 엔진은 크게 2개의 영역으로 구분됨
- 콜 스택
- 소스코드(전역 코드나 함수 코드 등) 평가 과정에서 생성된 실행 컨텍스트가 추가되고 제거되는 스택 자료구조
- 자바스크립트 엔진은 단 하나의 콜 스택을 사용하기 때문에 최상위 컨텍스트(실행 중인 실행 컨텍스트)가 종료되어 콜스택에서 제거되기 전까지는 다른 어떤 태스크도 실행되지 않음
- 힙
- 객체가 저장되는 메모리 공간. 콜 스택의 요소인 실행 컨텍스트는 힙에 저장된 객체를 참조함
- 동적 할당 공간이기에(객체의 크기는 정해져있지 않기때문에) 구조화되어 있지 않음
- 비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리(호출 스케줄링을 위한 타이머 설정, 콜백 함수의 등록 등)는 자바스크립트 엔진을 구동하는 환경인 브라우저 or Node.js가 담당함 ⇒ 태스크 큐, 이벤트 루프
태스크 큐, 이벤트 루프
태스크 큐
- setTimeout이나 setInterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역
이벤트 루프
- 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 태스크 큐에 대기중인 함수(콜백 함수, 이벤트 핸들러 등)가 있는지 반복해서 확인함
- 만약 콜 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적(FIFO)으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동시킴
- 즉, 태스크 큐에 일시 보관된 함수들은 비동기 처리 방식으로 동작함
예시로 보는 작동 과정
function foo() { console.log('foo'); } function bar() { console.log('bar'); } setTimeout(foo, 0); // 0초 (실제는 4ms) 후에 foo 함수가 호출된다. bar();
- 전역 코드가 평가되어 전역 실행 컨텍스트가 생성되고 콜 스택에 푸시됨
- setTimeout 함수 호출, 함수 실행 컨텍스트 생성 & 콜스택에 푸시
- setTimeout 함수가 실행되면 콜백 함수를 호출 스케줄링하고 종료되어 콜 스택에서 팝. 여기서 호출 스케줄링, 즉 타이머 설정과 타이머가 만료되면 콜백 함수를 태스크 큐에 푸시하는 것은 브라우저(nodejs)의 역할임(백그라운드로 따로 동작하는 것)
- 브라우저(↔ nodejs)의 수행(4-1)과 자바스크립트 엔진이 수행하는 4-2는 병행 처리됨
- 브라우저는 타이머를 설정하고 타이머의 만료를 기다림. 타이머가 만료되면 콜백 함수 foo 가 태스크 큐에 푸시됨(브라우저에 의해). 콜스택이 비어야 태스크 큐의 함수가 호출되므로 지연 시간이 정확하게 지켜지지 않을 수 있음
- bar 함수가 호출되어 함수 실행 컨텍스트 생성 & 콜스택에 푸시. 그리고 실행되면서 pop.
- 전역 코드 실행이 종료되고 전역 실행 컨텍스트가 콜 스택에서 팝됨. 콜 스택이 비어지게 됨
- 이벤트 루프가 콜 스택 비어있음을 감지하고, 태스크 큐의 콜백 함수를 콜 스택으로 이동시켜 실행시킴