42 비동기 프로그래밍

42.1 동기 처리와 비동기 처리

함수를 호출하면 함수 코드가 평가되어 함수 실행 컨텍스트가 생성된다. 이 때 생성된 함수 실행 컨텍스트는 실행 컨텍스트 스택 ( 콜 스택) 에 푸시되고 함수 코드가 실행된다.

함수 실행 컨텍스트가 콜 스택에 푸시되는 것은 함수 실행의 시작을 뜻한다.

자바스크립트 엔진은 단 하나의 실행 컨텍스트 스택을 갖는다.

// 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 호출

이처럼 JS엔진은 한 번에 하나의 태스크만 실행하는 [싱글 스레드] 방식으로 동작한다. 따라서 시간이 걸리는 태스크를 수행할 때에 [블로킹] (작업 중단)이 발생한다.

이처럼 현재 실행중인 태스크가 종료될 때까지 다음 태스크를 대기시키는 것을 [동기 처리]Synchronous라고 한다.

function foo() {
  console.log('foo');
}

function bar() {
  console.log('bar');
}

// 타이머 함수 setTimeout은 일정 시간이 경과한 이후에 콜백 함수 foo를 호출한다.
// 타이머 함수 setTimeout은 bar 함수를 블로킹하지 않는다.
setTimeout(foo, 3 * 1000);
bar();
// bar 호출 -> (3초 경과 후) foo 호출

위처럼 현재 실행중인 태스크가 종료되기 전이라도 다음 태스크를 곧바로 실행하는 것을 [비동기 처리]Asynchronous라고 한다.

비동기 처리를 위해서는 전통적으로 콜백 패턴을 사용했지만, 콜백 헬 등의 가독성을 저해하는 문제가 발생하며 비동기 처리 중 발생한 에러의 예외처리가 곤란하여 여러 비동기 처리를 한 번에 수행하기에 한계가 있다. 따라서 프로미스 패턴을 주로 사용한다.

타이머 함수인 setTimeout, setInterval, HTTP요청, 이벤트 핸들러 는 비동기 방식으로 동작한다.

42.2 이벤트 루프와 태스크 큐

JS는 싱글 스레드로 작동하나 여러 태스크가 동시에 작동하는 것처럼 느껴지는데, 이처럼 JS의 동시성을 지원하는 것이 바로 [이벤트 루프]이다.

자바스크립트 엔진 -> [힙](객체가 저장되는 메모리 공간) + [콜 스택] (순차적으로 실행하는 곳)

브라우저 or Node.js -> [이벤트 루프] (콜백함수 실행 타이밍 탐색기) + [태스크 큐] (비동기 콜백함수 대기장소) 를 제공

  1. JS 엔진은 힙, 콜 스택으로 구성된 싱글 스레드 방식으로 동작하기에 단순히 태스크가 요청되면 콜 스택을 통해 요청된 작업을 순차적으로 실행할 뿐이다.

  2. 비동기 함수는 콜 스택에 올라간 즉시 콜백 함수를 호출 스케줄링하고 pop 된다. -> 비동기 작업이 완료되면( =이는 브라우저가 수행한다. ex- Web API, DOM API, 타이머 함수, HTTP ,Ajax ... ) 콜백함수가 태스크 큐로 이동한다.

  3. 이 때 이벤트 루프는 계속해서 콜 스택과 태스크 큐가 비어있는지 살피게 되는데, 콜 스택이 비게 되면 (= 전역 코드 및 명시적 호출된 함수가 모두 종료하면 ) 태스크 큐의 콜백 함수들을 콜 스택에 푸시해 실행하게 된다.

즉, JS엔진은 싱글 스레드로 동작하지만 브라우저는 멀티 스레드로 동작한다.

Last updated