자바스크립트는 싱글 스레드인데 어떻게 비동기 작업이 가능한가

    자바스크립트는 싱글 스레드이지만 브라우저의 이벤트 루프와 WebAPIs 덕분에 비동기 작업이 가능하다

    자바스크립트의 런타임은 한 번에 하나의 일만 할 수 있는 싱글 스레드가 맞다.

    하지만 자바스크립트는 싱글 스레드인데 동시에 블로킹 없이 그 많은 작업을 처리할 수 있을까?

    그 이유는 순수하게 자바스크립트만 실행시키는 경우는 많지 않고 브라우저와 함께 실행하기 때문이다. 프론트엔드의 경우, 자바스크립트 코드는 반드시 브라우저 를 통해 실행시키게 된다. 싱글 스레드인 자바스크립트가 브라우저와 만나면서, 비동기 작업이 가능해진다.

    • 더 상세히 말하자면, 브라우저이벤트루프 를 통해 싱글 스레드인 자바스크립트가 비동기 작업을 할 수 있는 것이다. 그렇다. 비밀은 이벤트 루프 에 있다.

    자바스크립트는 (브라우저 환경에서) 이벤트 루프에 기반한 동시성 모델 을 가지고 있다

    • 이벤트루프콜스택에서 setTimeout 같은 비동기 함수가 호출되면, 해당 작업을 브라우저에 내장된 WebAPI 에게 넘긴다. (setTimeout은 브라우저가 제공하는 WebAPI이며, 자바스크립트 고유 기능이 아니기 때문에 ECMAScript에는 존재하지 않는다)
    • 그리고 Web API 안에서 타이머가 동작을 하거나 네트워크 통신을 진행하고, 코드가 실행될 준비가 되면 태스크 큐에 해당 작업을 넣어준다.
    • 콜 스택 이 비었으면 태스크 큐 에서 대기 중인 함수를 콜 스택 으로 불러와 실행한다.

    즉, 실제로 모든 함수가 순차적으로 싱글 스레드(콜 스택) 안에서 돌아가지만, 이런 시간 차가 너무 작기 때문에 비동기 작업이 여러 스레드에서 동시에 돌아갈 것이라고 착각하는 것이다.

    • 참고로 브라우저 밖인 Node.js에서도 비동기 IO를 지원하기 위해 libuv 라이브러리를 사용한다. 이 libuv 가 이벤트 루프를 제공하기 때문에, Node.js에서도 싱글 스레드인 자바스크립트가 비동기 작업을 할 수 있.

    이벤트 루프의 작동원리 더 자세히 살펴보기

    event loop

    이벤트 루프는 콜 스택과 각 (마이크로태스크 큐, 애니메이션 프레임, 매크로태스크 큐)를 감시하고 있다가 콜 스택이 비었을 경우 정해진 우선순위에 따라 에서 하나씩 꺼내 콜 스택에 추가해주는 역할을 한다.

    1. 콜 스택의 작업을 모두 처리한다.
    2. (마이크로태스크 큐) 콜 스택이 비었을 경우, 마이크로태스트 큐 를 확인하고 처리해야 할 작업이 있다면 콜 스택 에 넣고 처리한다.
    3. (애니메이션 프레임) 만약 마이크로태스크 큐 가 비었을 경우에는 애니메이션 프레임 를 확인하고 처리해야 할 작업이 있다면 콜 스택 에 넣고 처리한다.
    4. (매크로태스크 큐) 상기 과정을 거치고 난 후 마지막으로 매크로태스크 큐 를 확인하고 처리해야 할 작업이 있다면 콜 스택 에 넣고 처리한다.

    즉, 마이크로태스크 큐애니메이션 프레임매크로태스크 큐 의 순서로 비동기 함수의 콜백 함수를 처리한다.

    console.log("start")
    
    Promise.resolve().then(() => {
    	console.log("Promise")
    })
    
    requestAnimationFrame(() => {
    	console.log("rAF")
    })
    
    setTimeout(() => {
    	console.log("Timeout")
    }, 0)
    
    console.log("end")
    
    // 위 예제 실행시 로그 출력 결과는 start->end->promise->rAF->Timeout 이다.
    // 실행할때마다 rAF와 Timeout의 순서가 바뀌긴 한다.

    관련 용어 정의

    • 프로세스

      • 컴퓨터에서 실행중인 프로그램을 의미한다. 프로그램이 실행되면 메모리에 올라가서 프로세스로서 동작한다.
    • 스레드

      • 스레드는 프로세스 내에서 실행되는 작업흐름의 단위를 말한다. 보통 한 프로세스는 하나의 스레드를 갖고 있다. 프로세스의 환경에 따라 동시에 둘 이상의 스레드를 실행할 수 있으며, 이러한 방식을 멀티스레드라고 한다. 자바스크립트는 하나의 스레드만 갖고 있는 싱글 스레드 이다.
    • 동기(Synchronous)

      • 동기는 데이터의 요청과 결과가 한 자리에서 동시에 일어나는 것을 말한다. 요청을 하면 시간이 얼마나 걸리던지 요청한 자리에서 결과가 주어져야 한다. 즉, 어떤 코드의 실행이 완료되어야 그 다음 줄의 코드로 넘어가 실행할 수 있는 것을 의미한다.
    • 비동기(Asynchronous)

      • 비동기는 동시에 일어나지 않는다는 의미이다. 비동기 프로그래밍은 프로그램의 주 실행 흐름을 멈추어서 기다리는 부분 없이 바로 다음 작업을 실행할 수 있게 하는 방식이다. 즉, 코드의 실행 결과를 별도의 공간에 맡겨둔 뒤 결과를 기다리지 않고 다음 코드를 실행하는 병렬처리 방식이다.
    • Blocking

      • 블로킹은 느리게 실행되는 코드다. 예를 들어, 네트워크 요청이나 이미지 프로세싱은 느리다. 느린 동작이 스택에 남아있는 것을 보통 블로킹이라고 말한다.
      • 자바스크립트는 이렇게 느린 네트워크 요청을 하고는 다음 코드를 실행하기 위해 마냥 끝날 때까지 기다린다.
      • 문제는 브라우저가 모든 리퀘스트가 완료될 때까지 멈춰있다는 것이다. 즉, 동기적으로 실행되는 네트워크 요청이 콜 스택을 블로킹하여 브라우저가 다른 일들을 하는 것을 막고 있다.이 때문에 렌더링이나 다른 코드를 실행하지 못하고 그냥 멈춰버린다.
      • 이러한 블로킹 문제를 해결하기 위해 비동기 콜백을 사용한다.
    • 콜 스택

      • 콜 스택은 자바스크립트 코드가 실행되며 생성되는 실행 컨텍스트를 저장하는 자료구조이다. 또한 FILO(First In Last Out)으로 작동한다.
    • 마이크로태스크 큐

      • Promise를 통한 비동기 요청 시의 콜백 함수는 마이크로태스크 큐 에 대기한다.
      • 마이크로태스크의 우선순위는 일반 태스크(매크로태스크)보다 더 높다.
    • 애니메이션 프레임

      • requestAnimationFrame 에 의해 등록되는 자료 구조로서 requestAnimationFrame 의 콜백 함수가 대기하는 자료구조이다.
      • 애니메이션 프레임의 우선순위는 마이크로태스크보다는 낮고, 매크로태스크 보다는 높다.
    • 매크로태스크 큐

      • 비동기 함수가 실행된 후 콜백 함수가 대기하는 자료구조이다.
      • setTimeout(), setInterval()와 같은 태스크를 넘겨받는다.
    • Web APIs

      • 브라우저에서 제공하는 별도의 API이다. DOM, SVG, Fetch, Canvas, setTimeout, Web share, push, notification 등은 모두 자바스크립트가 아닌 브라우저에서 제공하는 API이다.
      • https://developer.mozilla.org/en-US/docs/Web/API

    Written by@Marco

    GitHub