Post

[모던 자바스크립트 Deep Dive]42장 비동기 프로그래밍

42.1 동기 처리와 비동기 처리

  • 함수 실행 과정
    1. 함수를 호출
    2. 함수 코드가 평가되어 함수 실행 컨텍스트 생성
    3. 생성된 함수 실행 컨텍스트를 실행 컨텍스트 스택(콜 스택)에 푸시
      1. 함수 실행의 시작을 의미
      2. 함수의 실행 순서는 실행 컨텍스트 스택으로 관리
    4. 함수 코드 실행
    5. 함수 코드 실행 종료
    6. 함수 실행 컨텍스트를 실행 컨텍스트 스택에서 팝
  • 블로킹(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)으로 태스크 큐에 있는 함수를 콜 스택으로 옮김
  • 브라우저
    • 브라우저는 멀티 스레드로 동작
    • 자바스크립트 엔진 외에 렌더링 엔진, 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();
  1. 전역 코드 평가 → 전역 실행 컨텍스트 생성 → 콜 스택에 푸시
  2. 전역 코드 실행 → setTimeout 함수 호출 → setTimeout 함수의 함수 실행 컨텍스트 생성 → 콜 스택에 푸시
    1. 현재 실행 중인 실행 컨텍스트 ⇒ setTimeout 함수의 함수 실행 컨텍스트
    2. 브라우저의 Web API(호스트 객체)인 타이머 함수도 함수이므로 함수 실행 컨텍스트를 생성
  3. setTimeout 함수 실행 → 콜백 함수를 호출 스케줄링 → 종료 → 콜 스택에서 팝
    1. 호출 스케줄링(타이머 설정 및 타이머 만료 시 콜백 함수를 태스크 큐에 푸시) ⇒ 브라우저의 역할
  4. 다음 두 단계는 병행 처리된다.
    1. 브라우저는 타이머를 설정 → 타이머 만료 시 콜백 함수 foo를 태스크 큐에 푸시
      1. delay로 0을 설정해도 최소 지연 시간은 4ms이므로 4ms 후에 푸시한다.
    2. 자바스크립트 엔진은 bar 함수 호출 → bar 함수의 함수 실행 컨텍스트 생성 → 콜 스택에 푸시
      1. 현재 실행 중인 실행 컨텍스트 ⇒ bar 함수의 함수 실행 컨텍스트
      2. 이후 bar 함수 종료 → 콜 스택에서 팝
  5. 전역 코드 실행 종료 → 전역 실행 컨텍스트를 콜 스택에서 팝
    1. 콜 스택에 실행 컨텍스트가 존재하지 않게 된다.
  6. 이벤트 루프가 빈 콜 스택을 감지 → 태스크 큐에서 대기 중인 콜백 함수 foo를 콜 스택에 푸시 → 콜백 함수 foo의 함수 실행 컨텍스트 생성 → 콜 스택에 푸시
    1. 현재 실행 중인 실행 컨텍스트 ⇒ foo 함수의 함수 실행 컨텍스트
    2. 이후 foo 함수 종료 → 콜 스택에서 팝
This post is licensed under CC BY 4.0 by the author.