42. 비동기 프로그래밍
2025. 1. 7. 14:31ㆍ기록/Modern JavaScript Deep Dive
- 목차
동기 처리와 비동기 처리
자바스크립트 엔진
- 단 하나의 실행 컨텍스트 스택을 가짐
- 한 번에 하나의 태스크만 실행할 수 있는 싱글 스레드 방식으로 동작함
- 처리에 시간이 걸리는 태스크를 실행하는 경우 블로킹(작업 중단)이 발생함
동기 처리
- 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식
- 장점: 실행 순서가 보장됨
- 단점: 앞선 태스크가 종료할 때까지 이후 태스크들이 블로킹됨
비동기 처리
- 현재 실행 중인 태스크가 종료되지 않은 상태여도 다음 태스크를 바로 실행하는 방식
- 장점: 블로킹이 발생하지 않음
- 단점: 실행 순서가 보장되지 않음
이벤트 루프와 태스크 큐
비동기 처리에서 소스코드의 평가/실행을 제외한 모든 처리는
자바스크립트 엔진을 구동하는 환경인 브라우저 또는 Node.js 가 담당함
예)
비동기 방식으로 동작하는 setTimeout 의 콜백 함수의 평가/실행은 자바스크립트 엔진이 담당함
호출 스케줄링을 위한 타이머 설정/콜백 함수 등록은 브라우저 또는 Node.js 가 담당함
콜 스택
- 소스코드 평가 과정에서 생성된 실행 컨텍스트가 추가/제거되는 스택 자료구조인 실행 컨텍스트 스택
힙
- 객체가 저장되는 메모리 공간
- 콜 스택의 요소인 실행 컨텍스트는 힙에 저장된 객체를 참조함
태스크 큐
- setTimeout 이나 setInterval 과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역
- 태스크 큐와는 별도로 프로미스의 후속 처리 메서드의 콜백 함수가 일시적으로 보관되는 마이크로태스크 큐도 존재함
이벤트 루프
- 자바스크립트의 동시성을 지원함 (많은 태스크가 동시에 처리되는 것처럼 느껴지게 함)
- 브라우저에 내장되어 있는 기능
- 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인함
- 콜 스택이 비어있고 태스크 큐에 대기 중인 함수가 있을 경우 순차적으로 대기 중인 함수를 콜 스택으로 이동시킴
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
setTimeout(foo, 0); // 0초 (실제는 4ms) 후에 foo 함수가 호출됨
bar();
- 전역 코드가 평가되어 전역 실행 컨텍스트가 생성되고 콜 스택에 푸시됨
- 전역 코드가 실행되기 시작하여 setTimeout 함수가 호출됨
- 이때 setTimeout 함수의 함수 실행 컨텍스트가 생성되고 콜스택에 푸시됨
- 브라우저의 Web API인 타이머 함수도 함수이므로 함수 실행 컨텍스트를 생성함
- setTimeout 함수가 실행되면 콜백 함수를 호출 스케줄링하고 종료되어 풀 스택에서 팝됨
- 이때 타이머 설정과 타이머가 만료되면 콜백 함수를 태스크 큐에 푸시하는 것은 브라우저의 역할
- 브라우저가 수행하는 4-1과 자바스크립트 엔진이 수행하는 4-2는 병행 처리됨
- 브라우저는 타이머를 설정하고 4ms 후 만료되면 콜백 함수 foo가 태스크 큐에 푸시됨
- 지연 시간이 4ms 이하인 경우 최소 지연 시간 4ms 가 지정됨
- 지연 시간 이후에 콜 스택이 비어야 콜백 함수가 호출되므로 정확히 지연 시간 후에 호출된다는 보장은 X
- bar 함수가 호출되어 함수 실행 컨텍스트가 생성되고 콜 스택에 푸시됨
- 이후 bar 함수가 종료되어 콜 스택에서 팝됨
- 이때 브라우저가 타이머를 설정한 후 4ms 가 경과했다면 foo 함수는 아직 태스크 큐에서 대기중임
- 브라우저는 타이머를 설정하고 4ms 후 만료되면 콜백 함수 foo가 태스크 큐에 푸시됨
- 전역 코드 실행이 종료되고 전역 실행 컨텍스트가 콜 스택에서 팝됨
- 이벤트 루프에 의해 콜 스택이 비어있음이 감지됨
- 태스크 큐에서 대기 중인 콜백 함수 foo가 이벤트 루프에 의해 콜 스택에 푸시됨
- 이후 foo 함수가 종료되어 콜 스택에서 팝됨
비동기 함수인 setTimeout의 콜백 함수는 태스크 큐에 푸시되어 대기하다가 콜 스택이 비게 되면,
즉 전역 코드/명시적으로 호출된 함수가 모두 종료하면 콜스택에 푸시되어 실행됨