42장 비동기 프로그래밍
동기 처리와 비동기 처리
- 함수 호출 → 함수 코드 평가됨 → 함수 실행 컨텍스트 생성됨.
- 함수 실행 컨텍스트가 실행 컨텍스트 스택에 푸시됨 → 함수 코드 실행됨 → 함수 코드 실행 종료 → 함수 실행 컨텍스트가 실행 컨텍스트 스택에서 팝되어 제거됨.
42-01
const foo = () => {};
const bar = () => {};
foo();
bar();
- JS 엔진은 단 하나의 실행 컨텍스트 스택을 가짐.
- === 동시에 2개 이상의 함수를 실행할 수 없음.
대기중인 태스크들은
- 현재 실행중인 실행 컨텍스트가 팝되어 실행 컨텍스트 스택에서 제거되면 === 현재 실행중인 함수가 종료하면 비로소 실행됨.
- 이처럼 JS 엔진은 한번에 하나의 태스크만 실행할 수 있는 싱글 스레드 방식으로 동작함.
- 한번에 하나의 태스크만 실행할 수 있기 때문에 처리에 시간이 걸리는 태스크를 실행하는 경우 **블로킹(작업중단)**이 발생함.
42-02
// sleep 함수는 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다.
function sleep(func, delay) {
// Date.now()는 현재 시간을 숫자(ms)로 반환한다.("30.2.1. Date.now" 참고)
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 호출
- 동기처리 : 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식
- 동기 처리 받식은 실행 순서가 보장된다는 장점이 있지만, 앞선 태스크가 종료할 때까지 이후 태스크들이 블로킹되는 단점이 있음.
- setTimeout으로 수정
42-03
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 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작함.
이벤트 루프와 태스크 큐
- JS의 특징 중 하나는 싱글 스레드로 동작한다는 것.
- 하지만 많은 태스크가 동시에 처리되는 것처럼 느껴짐.
- JS의 동시성을 지원하는 것이 바로 이벤트 루프.
- 대부분의 JS 엔진은 크게 2개의 영역으로 구분
1 ) 콜 스택
- 소스코드 평가 과정에서 생성된 실행 컨텍스트가 추가되고 제거되는 스택 자료구조인 실행 컨텍스트 스택
2 ) 힙
- 객체가 저장되는 메모리 공간.
- 콜 스택의 요소인 실행 컨텍스트는 힙에 저장된 객체를 참조함.
- JS 엔진은 단순히 태스크가 요청되면 콜 스택을 통해 요청된 작업을 순차적으로 실행할 뿐임.
- 비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리는 JS 엔진을 구동하는 환경인 브라우저 또는 Node.js가 담당함.
- 이를 위해 브라우저 환경은 태스크 큐와 이벤트 루프를 제공함.
1 ) 태스크 큐
- 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역.
- 마이크로태스크 큐 : 프로미스의 후속 처리 메서드의 콜백함수가 일시적으로 보관됨.
2 ) 이벤트 루프
- 콜 스택에 현재 실행중인 실행 컨텍스트가 있는지, 그리고 태스크 큐에 대기중인 함수가 있는지 반복해서 확인함.
- 만약 콜 스택이 비어있고 태스크 큐에 대기중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기중인 함수를 콜 스택으로 이동시킴.
42-04
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
setTimeout(foo, 0); // 0초(실제는 4ms) 후에 foo 함수가 호출된다.
bar();
- 비동기 함수인 setTimeout의 콜백 함수는 태스크 큐에 푸쉬되어 대기하다가 콜 스택이 비게 되면, 다시 말해 전역 코드 및 명시적으로 호출된 함수가 모두 종료하면 비로소 콜 스택에 푸쉬되어 실행됨.
- JS엔진은 싱글 스레드로 동작하지만 브라우저는 멀티 스레드로 동작함.
- setTimeout 함수의 두가지 기능인 타이머 설정과 타이머가 만료하면 콜백 함수를 태스크 큐에 등록하는 처리는 JS 엔진이 아니라 브라우저가 실행함.
- 그래서 비동기 처리를 할 수 있게 되는 것임.(원래 JS 엔진은 싱글 스레드 이므로)
'JS' 카테고리의 다른 글
모던 자바스크립트 Deep Dive - REST API (0) | 2024.02.15 |
---|---|
모던 자바스크립트 Deep Dive - Ajax (0) | 2024.02.14 |
모던 자바스크립트 Deep Dive - 타이머 (0) | 2024.02.12 |
모던 자바스크립트 Deep Dive - 이벤트 (0) | 2024.02.11 |
모던 자바스크립트 Deep Dive - DOM 2 (0) | 2024.02.10 |
댓글