자바스크립트는 싱글 스레드인데 어떻게 비동기 작업이 가능한가
August 30, 2022
자바스크립트는 싱글 스레드이지만 브라우저의 이벤트 루프와 WebAPIs 덕분에 비동기 작업이 가능하다
자바스크립트의 런타임은 한 번에 하나의 일만 할 수 있는 싱글 스레드
가 맞다.
하지만 자바스크립트는 싱글 스레드인데 동시에 블로킹 없이 그 많은 작업을 처리할 수 있을까?
그 이유는 순수하게 자바스크립트만 실행시키는 경우는 많지 않고 브라우저
와 함께 실행하기 때문이다. 프론트엔드의 경우, 자바스크립트 코드는 반드시 브라우저
를 통해 실행시키게 된다. 싱글 스레드인 자바스크립트가 브라우저와 만나면서, 비동기
작업이 가능해진다.
- 더 상세히 말하자면,
브라우저
의이벤트루프
를 통해 싱글 스레드인 자바스크립트가 비동기 작업을 할 수 있는 것이다. 그렇다. 비밀은이벤트 루프
에 있다.
자바스크립트는 (브라우저 환경에서) 이벤트 루프에 기반한 동시성 모델 을 가지고 있다
이벤트루프
는콜스택
에서 setTimeout 같은 비동기 함수가 호출되면, 해당 작업을 브라우저에 내장된WebAPI
에게 넘긴다. (setTimeout은 브라우저가 제공하는 WebAPI이며, 자바스크립트 고유 기능이 아니기 때문에 ECMAScript에는 존재하지 않는다)- 그리고
Web API
안에서 타이머가 동작을 하거나 네트워크 통신을 진행하고, 코드가 실행될 준비가 되면태스크 큐
에 해당 작업을 넣어준다. 콜 스택
이 비었으면태스크 큐
에서 대기 중인 함수를콜 스택
으로 불러와 실행한다.
즉, 실제로 모든 함수가 순차적으로 싱글 스레드(콜 스택) 안에서 돌아가지만, 이런 시간 차가 너무 작기 때문에 비동기 작업이 여러 스레드에서 동시에 돌아갈 것이라고 착각하는 것이다.
- 참고로 브라우저 밖인 Node.js에서도 비동기 IO를 지원하기 위해
libuv
라이브러리를 사용한다. 이libuv
가 이벤트 루프를 제공하기 때문에, Node.js에서도 싱글 스레드인 자바스크립트가 비동기 작업을 할 수 있.
이벤트 루프의 작동원리 더 자세히 살펴보기
이벤트 루프는 콜 스택
과 각 큐
(마이크로태스크 큐, 애니메이션 프레임, 매크로태스크 큐)를 감시하고 있다가 콜 스택
이 비었을 경우 정해진 우선순위에 따라 큐
에서 하나씩 꺼내 콜 스택
에 추가해주는 역할을 한다.
콜 스택
의 작업을 모두 처리한다.- (마이크로태스크 큐)
콜 스택
이 비었을 경우,마이크로태스트 큐
를 확인하고 처리해야 할 작업이 있다면콜 스택
에 넣고 처리한다. - (애니메이션 프레임) 만약
마이크로태스크 큐
가 비었을 경우에는 애니메이션 프레임 를 확인하고 처리해야 할 작업이 있다면콜 스택
에 넣고 처리한다. - (매크로태스크 큐) 상기 과정을 거치고 난 후 마지막으로
매크로태스크 큐
를 확인하고 처리해야 할 작업이 있다면콜 스택
에 넣고 처리한다.
즉, 마이크로태스크 큐
→ 애니메이션 프레임
→ 매크로태스크 큐
의 순서로 비동기 함수의 콜백 함수를 처리한다.
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