ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Group Study, 모던 자바스크립트 Deep Dive] - 42 비동기 프로그래밍
    Front-end/Javascript 2023. 3. 26. 19:22
    반응형

    Group study background

    나만 그런건지는 모르겠지만, 실무를 하다보면 잊어버리는 개념들이 있다.

    가끔 FE 뉴비인분들에게 질문을 받는데, 아리송 할때만큼 쪽팔릴때가 없었다.

    인간은 망각의 동물이라고 교수님께서 말씀하셨지만 반복 학습의 힘을 믿는다. React 오픈카톡방에서 모집한 스터디원분들과 함께 "모던 자바스크립트 Deep Dive" 1권 톺아보기를 시작한다!

     

    정보 전달용이 아닌 개인 스터디 레코딩용 포스트입니다.


    42.1 동기 처리와 비동기 처리

    [예제 42-01]

    const foo = () => {};
    const bar = () => {};
    
    foo();
    bar();

    [그림 42-01]

     함수가 실행되려면 "함수 코드 평가 과정"에서 생성된 함수 실행 컨텍스트가 실행 컨텍스트에 푸시되어야 한다. 즉, 실행 컨택스트 스택에 함수 실행 컨택스트가 푸시되는 것은 바로 함수 실행의 시작을 의미한다. 함수가 호출된 순서대로 순차적으로 실행되는 이유는 호출된 순서대로 함수 실행 컨텍스트가 실행 컨텍스트 스택에 푸시되기 때문이다.

     JS 엔진은 단 하나의 실행 컨텍스트 스택을 갖는다. "실행 중인 실행 컨텍스트"를 제외한 모든 실행 컨텍스트는 모두 실행 대기 중인 테스큳링다. 대기 중인 테스크들은 현재 실행 중인 실행 컨텍스트가 팝되어 실행 컨택스트에서 제거되면, 비로소 실행되기 시작한다.

     JS 엔진은 하나의 태스크만 실행할 수 있는 싱글 스레드 방식이기 때문에 시간이 걸리는 태스크를 실행하는 경우, 블로킹(blocking -작업 중단)이 발생한다. 예를 들어 sleep(func, 10 * 1000)을 사용하게 되면 약 10초 후에 func 함수를 호출한다. 이때 sleep 이후 코드 라인의 함수들은 10초동안 호출되지 못하고 블로킹(작업 중단)된다.

    이렇게 한 태스크가 종료될 때까지 다음 실행될 태스크가 대기하는 방식을 동기처리(synchronous)라 한다. 동기 처리 방식은 태스크를 순서대로 하나씩 처리하므로 실행 순서가 보장되는 장점이 있지만 블로킹되는 단점이 있다.


    42.2 이벤트 루프와 태스크 큐

     JS 엔진은 "싱글 스레드"이기 때문에 하나씩 태스크를 처리해야 하는데, 우리가 사용하는 브라우저는 그렇지 않은듯 하다. 자바스크립트의 동시성(concurrecny)를 지원하기 위해 사용되는 것이 이벤트 루프(event loop)이다. 이 개념 또한 FE 개발자라면 꼭 알아야 할 개념 중 하나이며, 테크 인터뷰에서 등장하는 단골 토픽 중 하나이다.

    [그림 42-02]

    구글의 V8 JS 엔진을 비롯한 대부분의 엔진은 크게 2개의 영역으로 구분할 수 있다.

    • 콜 스택(call stack)
      • 소스코드(전역 코드나 함수 코드 등) 평가 과정에서 생성된 실행 컨택스트가 추가되고 제거되는 스택 자료구조인 실행 컨택스트 스택
      • 함수를 호출하면 함수 실행 컨택스트가 순차적으로 콜 스택에 푸시되어 순차적으로 실행 됨. 하나의 콜 스택을 사용하기 때문에 최상위 실행 컨택스트가 종료되어 콜스택에서 제거되기 전까지는 다른 태스크들은 기다려야 한다.
    • 힙(heap)
      • 객체가 저장되는 메모리 공간. 콜 스택의 요소인 실행 컨택스트는 힙에 저장된 객체를 참조
      • 메모리에 값을 저장하려면 먼저 값을 저장할 메모리 공간의 크기를 결정해야 한다. 객체는 원시 값과는 달리 크기가 정해져 있지 않으므로 할당해야 할 메모리 공간의 크기를 런타임에 결정(동적 할당)해야 한다. 따라서 객체가 저장되는 메모리 공간의 힙은 구조화되어 있지 않다.

    비동기 처리에서 소스코드 평가와 실행을 제외한 모든 처리는 JS엔진을 구동하는 환경인 브라우저 또는 Node.js가 담당한다. 예를 들어, 비동기 방식으로 동작하는 setTimeout의 콜백 함수의 평가와 실행은 JS엔진이 담당하지만 호출 스케줄링을 위한 타이머 설정과 콜백 함수의 등록은 브라우저 또는 Node.js가 담당한다. 

    • 태스크 큐(task queue/event queue/callback queue)
      • setTimout이나 setInterval와 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역. 태스크 큐와는 별도로 프로미스의 후속 처리 메서드의 콜백 함수가 일시적으로 보관되는 Microstack queue도 존재한다.
    • 이벤트 루프(event loop)
      • 이벤트 루프는 콜 스택에 현재 실행 중인 실행 컨택스트가 있는지, 그리고 태스크 큐에 대기 중인 함수(콜백 함수, 이벤트 핸들러 등)가 있는지 반복해서 확인.
      • If(콜 스택이 빔 && 대기 중인 함수 존재) -> 이벤트 루프는 순차적으로(FIFO) 태스크 큐에 대기 중인 함수를 콜 스택으로 이동

    [예제 42-02]

    function foo() {
      console.log('foo')
    }
    
    function bar() {
      console.log('bar')
    }
    
    setTimeout(foo, 0); // 0초(실제는 4ms) 후에 foo 함수 호출
    bar();

    코드 실행 순서

    1. 전역 코드가 평가되어 전역 실행 컨텍스트가 생성되고 콜 스택에 push
    2. 전역 코드가 실행되기 시작하여 setTimeout 함수 호출
      1. setTimout 함수의 함수 실행 컨텍스트가 생성되고 콜 스택에 푸시되어 현재 실행 중인 실핸 컨텍스트가 됨. 브라우저의 WEB API인 타이머 함수도 함수이므로 함수 실행 컨텍스트 생성
    3. setTimemout 함수라 실행되면 콜백 함수를 호출 스케줄링하고 종료되어 콜 스택에서 pop. 타이머 설정과 타이머가 만료되면 콜백 함수를 task queue에 푸시하는 것은 브라우저의 몫
    4. 브라우저가 수행하는 4-1과 js엔진이 수행하는 4-2는 병행 처리 됨.
      1. 브라우저는 타이머를 설정하고 타이머의 만료를 기다림. 이후 타이머가 만료되면 콜백 함수 foo가 태스크 큐에 push. 위 예제의 경우 지연시간(delay)이 0이지만 지연 시간이 4ms 이하인 경우 최소 지연 시간 4ms가 지정 됨. 따라서 4ms 호에 콜백 함수 foo가 task queue에 push되어 대기하게 됨.  이 예시처럼 setTimeout 함수로 호출 스케줄링한 콜백 함수는 정확히 지연 시간 이후에 콜백 함수가 호출된다는 보장이 없음. 지연 시간 이후에 콜백 함수가 task queue에 push되어 대기하게 되지만 콜 스텍이 비어야 호출되므로 실제로는 약간의 시간차가 발생
      2. bar 함수가 호출되어 bar 함수의 함수 실행 컨텍스트가 생성되고 콜 스택에 push되어 현재 실행 중인 실행 컨텍스트가 된다. 이후 bar 함수가 종료되어 콜 스택에서 pop 되고, 타이머를 설정한 후 약 4ms가 결과했다면 foo 함수는 아직 태스크 큐에서 대기 중
    5. 전역 코드 실행이 종료되고 전역 실행 컨텍스트가 콜 스택에서 pop 된다. 이로서 콜 스텍에는 아무런 실행 컨텍스트도 존재X
    6. 이벤트 루프에 의해 콜 스텍이 비어 있음이 감지되고 테스크 큐에서 대기 중인 콜백 함수 foo가 이벤트 루프에 의해 콜 스택에 push 된다. 즉, 콜백 함수 foo의 함수 실행 컨텍스트가 생성되고 콜 스택에 push되어 현재 실행 중인 실행 컨켁스트가 됨.

    Summary

    • 비동기 함수인 setTimeout의 콜백 함수는 태스크 큐에 푸시되어 대기하다가 콜 스택이 비게 되면, 전역 코드 및 명시적으로 호출된 함수가 모두 종료하면 콜 스택에 푸시되어 실행 됨.
    • JS 엔진은 싱글 스레드 방식 동작
    • 브라우저는 멀티 스레드 방식 동작

     

     

    반응형

    댓글

Designed by Tistory.