동기 처리
자바스크립트는 기본적으로 싱글 스레드 기반의 언어이다. 따라서 하나의 호출 스택만을 이용해 한 번에 한 가지의 일만 처리할 수 있다. 코드들은 순차적으로 실행되며, 이전 작업이 완료되어야 다음 작업으로 넘어간다. 이를 동기(Synchronomous)처리라 한다.
비동기 처리
반대로 비동기(Asynchromonous) 처리란, 특정 코드의 연산이 끝나지 않아도 다음 코드를 처리할 수 있는 방식을 말한다. 자바스크립트에는 fetch()를 통한 HTTP 요청이나, setTimeout을 통한 타이머 구현같은 다양한 비동기 함수가 존재한다. 이러한 처리들이 동기적으로 처리된다면 이용자 입장에서는 마우스를 집어던질지도 모른다. 따라서 비동기적인 호출이 있을 때 작업을 시작하긴 하지만, 즉시 복귀하여 다른 이벤트가 동기적 코드들을 처리하게 된다.
예전엔 비동기 함수를 구현하기 위해 이벤트에 기반한 콜백함수 방식을 주로 사용하였다. 그러나 가독성이 좋지 않기도 하고, 함수들이 깊게 중첩되어 있는 콜백 지옥(callback hell)문제를 불러올 수 있어 최근에는 사용하지 않는다고 한다. (Promise를 통해 비동기 처리가 완료된 이후 시점의 제어가 이루어진다고 함, Promise와 Async/Await는 다음에!)
비동기 처리는 언제 필요할까?
주로 결과를 즉시 얻을 수 없고, 일정 시간을 필요로 하는 로직들을 비동기 처리를 통해 구현한다. 예를 들어 API 호출같은 네트워크 요청, Node.js 환경에서 파일을 읽고 쓰는 경우(fs, rl), setTimeout() 같은 타이머 함수 등이 있다.
자바스크립트의 처리 순서
호출스택
호출스택(Call Stack)이란, 코드의 실행 컨텍스트*를 저장하는 스택 구조의 메모리 공간이다. 자바스크립트에서 동기적인 작업들이 호출 스택을 통해 순차적으로 처리된다. 함수가 호출되면 해당 함수의 실행 컨텍스트가 스택에 쌓이고, 함수가 반환되면 해당 실행 컨텍스트가 스택에서 제거된다. 이는 LIFO(Last in, First out) 방식으로 동작한다.
*실행컨텍스트 : 자바스크립트 코드 실행환경. 코드 실행 시 변수, 함수, 스코프를 관리하는 데 사용된다. 변수 생성, 초기화, 함수 호출, 스코프 체인을 통한 변수 검색 및 this 값 설정 등의 역할을 수행한다.
이벤트 큐와 이벤트 루프
이벤트 큐는 비동기 작업의 콜백 함수들이 대기하는 큐이다. 호출 스택에서의 동기적 처리가 끝나면 비동기적인 작업의 콜백 함수를 이벤트 루프를 통해 호출 스택으로 이동시킨다. 호출 스택이 비어있지 않으면 이벤트 루프는 대기하며, 이는 FIFO(First in, First out) 방식으로 동작된다.
마이크로태스크 큐
주로 Promise의 then 메서드나 Async/Await 같은 지정 콜백함수들이 대기하는 큐이다. 일반적인 비동기 콜백함수(매크로태스크 큐에 존재)보다 우선순위가 높기 때문에, 동기 함수 처리가 끝나고 호출 스택이 비어있다면 비동기 중 가장 먼저 처리된다.
매크로태스크 큐
일반적인 비동기 작업의 콜백함수들이 대기하는 큐이다. setTimeout이나 setInterval 같은 Web API나, I/O 작업들이 매크로태스크 큐에 등록된다. 이들은 호출 스택이 완전히 비어있을 때에만 이벤트 루프를 타고 이동해 실행된다. 매크로태스크가 실행되면서 새롭게 생성된 매크로태스크는 실행되지 않고, 다음 이벤트 루프가 실행되는 시점에 처리가 이루어진다.
Reference