[모던 자바스크립트 Deep Dive]42장 비동기 프로그래밍
42.1 동기 처리와 비동기 처리
- 함수 실행 과정
- 함수를 호출
- 함수 코드가 평가되어 함수 실행 컨텍스트 생성
- 생성된 함수 실행 컨텍스트를 실행 컨텍스트 스택(콜 스택)에 푸시
- 함수 실행의 시작을 의미
- 함수의 실행 순서는 실행 컨텍스트 스택으로 관리
- 함수 코드 실행
- 함수 코드 실행 종료
- 함수 실행 컨텍스트를 실행 컨텍스트 스택에서 팝
- 블로킹(blocking, 작업 중단)
- 자바스크립트 엔진은 싱글 스레드 방식으로 동작한다.
- 즉 한 번에 하나의 태스크만 실행하기 때문에 오래 걸리는 태스크를 실행하면 블로킹이 발생한다.
- 동기(synchronous) 처리
- 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식
- 순서대로 태스크를 처리하므로 실행 순서를 보장
앞선 태스크 종료 전까지 이후의 태스트를 블로킹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// sleep 함수는 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. function sleep(func, delay) { // Date.now()는 현재 시간을 숫자(ms)로 반환한다. const delayUntil = Date.now() + delay; // 현재 시간(Date.now())에 delay를 더한 delayUntil이 // 현재 시간보다 작으면 계속 반복한다. while (Date.now() < delayUntil); // 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. func(); } function foo() { console.log('foo'); } function bar() { console.log('bar'); } // sleep 함수는 3초 이상 실행된다.. sleep(foo, 3 * 1000); // bar 함수는 sleep 함수의 실행이 종료된 이후에 호출되므로 3초 이상 블로킹된다. bar(); // (3초 경과 후) foo 호출 -> bar 호출
- 비동기(asynchronous) 처리
- 현재 실행 중인 태스크가 종료되지 않더라도 다음 태스크가 실행되는 방식
- 실행 순서가 보장되지 않음
- 블로킹이 발생하지 않음
1 2 3 4 5 6 7 8 9 10 11 12 13
function foo() { console.log('foo'); } function bar() { console.log('bar'); } // 타이머 함수 setTimeout은 일정 시간이 경과한 이후에 콜백 함수 foo를 호출한다. // 타이머 함수 setTimeout은 bar 함수를 블로킹하지 않는다. setTimeout(foo, 3 * 1000); bar(); // bar 호출 -> (3초 경과 후) foo 호출
- 전통적으로 콜백 패턴 사용 ⇒ 콜백 헬 발생 ⇒ 프로미스 등장
setTimeout
,setInterval
, HTTP 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작
42.2 이벤트 루프와 태스크 큐
- 비동기 처리에서 소스코드 평가와 실행을 제외한 모든 처리는 브라우저/Node.js가 담당한다.
- 예)
setTimeout
콜백 함수의 평가와 실행 ⇒ 자바스크립트 엔진 - 예) 호출 스케줄링을 위한 타이머 설정과 콜백 함수의 등록 ⇒ 브라우저/Node.js
- 예)
- 자바스크립트 엔진의 구성 요소
- 콜 스택
- 실행 컨텍스트 스택
- 소스코드 평가 과정에서 생성된 실행 컨텍스트가 추가/제거되는 스택 자료구조
- 함수 호출 시 콜 스택에 함수 실행 컨텍스트 푸시
- 자바스크립트 엔진은 단 하나의 콜 스택 사용 ⇒ 최상위 실행 컨텍스트(실행 중인 실행 컨텍스트)가 종료되어 콜 스택에서 제거되기 전까지 다른 태스크는 실행되지 않는다.
- 힙
- 객체가 저장되는 구조화되어 있지 않은 메모리 공간
- 실행 컨텍스트는 힙에 저장된 객체를 참조
- 객체는 크기가 정해져 있지 않으므로 메모리 공간 크기를 런타임에 결정(동적 할당)
- 콜 스택
- 브라우저 환경의 구성 요소
- 태스크 큐(task queue, event queue, callback queue)
- 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역
- 이벤트 루프
- 자바스크립트의 동시성(concurrency)을 지원
- 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인
- 콜 스택이 비어있고 태스크 큐에 대기 중인 함수가 존재하는 경우 순차적(FIFO)으로 태스크 큐에 있는 함수를 콜 스택으로 옮김
- 태스크 큐(task queue, event queue, callback queue)
- 브라우저
- 브라우저는 멀티 스레드로 동작
- 자바스크립트 엔진 외에 렌더링 엔진, Web API 제공
- Web API: DOM API, 타이머 함수, HTTP 요청(Ajax)과 같은 비동기 처리 포함
예제
1
2
3
4
5
6
7
8
9
10
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
setTimeout(foo, 0); // 0초(실제는 4ms) 후에 foo 함수가 호출된다.
bar();
- 전역 코드 평가 → 전역 실행 컨텍스트 생성 → 콜 스택에 푸시
- 전역 코드 실행 →
setTimeout
함수 호출 →setTimeout
함수의 함수 실행 컨텍스트 생성 → 콜 스택에 푸시- 현재 실행 중인 실행 컨텍스트 ⇒
setTimeout
함수의 함수 실행 컨텍스트 - 브라우저의 Web API(호스트 객체)인 타이머 함수도 함수이므로 함수 실행 컨텍스트를 생성
- 현재 실행 중인 실행 컨텍스트 ⇒
setTimeout
함수 실행 → 콜백 함수를 호출 스케줄링 → 종료 → 콜 스택에서 팝- 호출 스케줄링(타이머 설정 및 타이머 만료 시 콜백 함수를 태스크 큐에 푸시) ⇒ 브라우저의 역할
- 다음 두 단계는 병행 처리된다.
- 브라우저는 타이머를 설정 → 타이머 만료 시 콜백 함수
foo
를 태스크 큐에 푸시delay
로 0을 설정해도 최소 지연 시간은 4ms이므로 4ms 후에 푸시한다.
- 자바스크립트 엔진은
bar
함수 호출 →bar
함수의 함수 실행 컨텍스트 생성 → 콜 스택에 푸시- 현재 실행 중인 실행 컨텍스트 ⇒
bar
함수의 함수 실행 컨텍스트 - 이후
bar
함수 종료 → 콜 스택에서 팝
- 현재 실행 중인 실행 컨텍스트 ⇒
- 브라우저는 타이머를 설정 → 타이머 만료 시 콜백 함수
- 전역 코드 실행 종료 → 전역 실행 컨텍스트를 콜 스택에서 팝
- 콜 스택에 실행 컨텍스트가 존재하지 않게 된다.
- 이벤트 루프가 빈 콜 스택을 감지 → 태스크 큐에서 대기 중인 콜백 함수
foo
를 콜 스택에 푸시 → 콜백 함수foo
의 함수 실행 컨텍스트 생성 → 콜 스택에 푸시- 현재 실행 중인 실행 컨텍스트 ⇒
foo
함수의 함수 실행 컨텍스트 - 이후
foo
함수 종료 → 콜 스택에서 팝
- 현재 실행 중인 실행 컨텍스트 ⇒
This post is licensed under CC BY 4.0 by the author.