Runtime 환경에서 비동기

zwundzwzig·2022년 11월 10일
2

[javascript]

목록 보기
12/13

자바스크립트에서 비동기가 어떻게 구현되는지 알아보자.

Node.JS는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript Runtime입니다.


➡️ Node.js 공식 문서

Asynchronous 비동기가 뭐더라? ⚙️

먼저 호출된 코드의 종료 시점이 순서대로 처리되지 않을 수 있다는 개념이다.

그럼 런타임은 뭘까? 📚

Runtime은 컴퓨터 과학에서 컴퓨터 프로그램이 실행되고 있는 동안의 동작을 말한다.
컴퓨터 언어 안에 쓰인 프로그램을 관리하기 위해, 특정한 컴파일러나 가상 머신이 사용하는 기본 코드의 라이브러리나 프로그램을 통틀어 런타임 라이브러리라고도 일컫는다.
➡️ Wikipedia

스크립트 언어인 자바스크립트는 특정 런타임 환경에서만 구동 가능한데, 브라우저뿐만 아니라 Node에서도 동작이 가능한 것이고 런타임은 그러한 환경을 일컫는다.

비동기 코드가 어떻게 구현되는지 알기 위해, 그에 필요한 최소한의 JS 런타임 환경 요소를 알아보자!

런타임은 V8 엔진, 웹 APIs, 이벤트 루프, 콜백 큐 그리고 렌더 큐로 구성된다.

참고로, Node.js의 libuv는 HTML 스펙을 완벽히 따르지는 않기 때문에, 브라우저 환경의 이벤트 루프와 상세 구현이 조금씩 다르고 각 브라우저마다도 조금씩 다르다고 한다. 오늘 포스팅에선 크롬을 기준으로 하겠다.

엔진 V8

자바스크립트 엔진인 V8은 ECMAScript를 가동하는데, 자체적으로 실행하지 못하는 인터프리터 언어인 자바스크립트가 실행하도록 도와준다.

V8로 인해 코드를 브릿지 없이 바이트 코드로 바로 변환하고 캐싱한다.

V8에는 크게 Memory HeapCall Stack이 존재하고, V8 엔진 소스 안에는 하나의 힙과 하나의 콜 스택만 있다.

콜스택이 하나라는 말은, 스레드도 하나라는 뜻. 그렇기에 자바스크립트는 싱글스레드 언어인 것! 그래서 자바스크립트는 한 번에 한 가지 일만 수행할 수 있다.

여기서 다룰 콜스택은 수행할 함수들을 순차적으로 담아 처리하는, 런타임에서 주된 기능을 담당하고 있으며 하나이기 때문에 비동기 작업을 다루지 않는다.

참고로,

  • 메모리힙은 메모리 할당이 이뤄지는 곳
  • 힙(heap)은 구조화되지 않은 넓은 메모리 영역(!객체 ||!변수 || !함수 등)이 할당되는 곳

으로만 알고 넘어가자.

Call Stack

콜스택은 동기적으로 코드가 쌓이는 곳이다.

stack : 자료구조 형태 중 하나, 선입후출(LIFO, Last In First Out) 방식.

파일 하나를 호출하면, 해당 파일은 (ex. main) 맨 먼저 스택에 쌓인다.
이 곳은 입구와 출구가 같다.
이후 해당 파일의 코드 및 함수가 호출될 때마다 차례로 스택에 쌓이고,
함수들이 실행되는 동안 main 함수는 맨 밑에 위치한다.
그러다 모든 함수가 리턴되면 그때 main 파일도 리턴되며 스택을 빠져나오며 해당 파일의 로직이 끝난다.

여기서 질문?
➡️ 아까 분명 V8 엔진은 비동기를 처리하지 못한다고 했다.
➡️ 그럼 비동기 함수 친구들은 어디로 갔을까?
➡️ 바로 엔진 바깥에서 비동기 함수를 처리하는 환경으로 간다.

Web APIs (Node 환경에선 C++ APIs)

그 환경은 브라우저 상에선 Web API, 서버 환경에선 C++ API or Node API이다.

V8 엔진 소스코드에 존재하지 않고, 런타임 환경에 존재하는 별도의 API
setTimeOut(), setInterval(), setImmediate(), AJAX(HTTP 요청, XML 등), DOM 이벤트 등의 비동기를 처리한다.

아까 콜스택이 처리한 (실제론 처리한 게 아니라 API에 넘겨주며 처리했다고 판단될) 비동기 함수를 받는다.
이 API 환경에선 해당 함수의 조건이 충족되기 전까지 해당 함수가 관리된다.
이후 조건이 충족되면 비동기식 콜백 함수를 '어딘가'로 밀어넣는다.

여기서 질문?
➡️ 그래서 어디로?
➡️ 물론, API는 스택으로 함수를 보내진 않는다.

Callback Queue

바로 이곳으로 콜백함수가 넘어온다.

Queue : 자료구조 형태 중 하나, 선입선출(FIFO, Frist In Frist OUT) 방식.

콜백 큐는 API에서 받은 콜백 함수를 순서대로 대기 시킨다.
이 곳은 입구와 출구가 따로 분리돼 있고, 입구로 들어와 출구를 향해 순서대로 대기열에 위치한다.
즉, 대기열에 가장 오래 머문 코드가 먼저 빠져 나가는 것.
빠져나간 코드는 맨 처음에 콜스택으로 돌아가 로직을 구현한다.

여기서 질문?
➡️ 큐에 담긴다고 함수가 실행되진 않는다?
➡️ 대기열에 머물던 코드가 '어떻게' 다시 콜스택으로 돌아가나?

Microtask Queue vs Macrotask Queue

콜백 큐는 몇 개의 계층 구조로 이뤄져 있다. 그 중 마이크로테스크 큐와 (마크로)테스크 큐를 알아보자.

Microtask Queue

API에서 다루지 않는, 아래 목록의 콜백을 다룬다.

  • promise 콜백
  • async 콜백
  • process.nextTick() 함수
  • queueMicrotask() 함수

위 목록들을 사용한 함수를 대기열에 담고 있다가 call stack이 빌 때 콜스택으로 올려 실행시킨다. 비슷한 역할을 하는 잡 큐가 있다.

Macrotask Queue

Timer 함수들이 여기에 위치한다.

  • setTimeout
  • setInterval
  • setImmediate

위 목록들을 사용한 함수를 대기열에 담고 있다가 call stack과 Microtask Queue가 빌 때 콜스택으로 올려 실행시킨다.

즉, 마이크로테스크 큐보다 아래 계층에 위치한 큐이다. 테스크 큐라 불린다. 비슷한 역할을 하는 이벤트 큐가 있다.

Event Loop

이벤트 루프는 런타임 환경에서 콜백 함수를 사용할 때 핵심적인 역할을 수행한다.

이벤트 루프는 기본적으로 콜스택과 콜백큐의 상태를 체크한다.
그러다가 콜스택에 있던 모든 함수가 리턴되면,
콜백큐에서 대기하던 함수 중 '가장 준비 완료된 지 오래된 코드'를 콜스택으로 올려준다.
이 과정이 반복되며 이러한 행동을 틱(tick) 이라 부른다.

싱글 스레드 프로그래밍 언어 자바스크립트는 이벤트 루프를 사용하여 동시성을 지원하기 때문에 개발자로 하여금 여러 작업이 동시에 진행되는 것처럼 느끼게 해준다!!

참고로, 엔진, API, 큐 각각 다른 스레드이다.

참조

profile
개발이란?

0개의 댓글