JS가 비동기적 통신을 할 수 있는 이유에 대해 알아보자.
또한 event loop, blocking, callstack, heap 등 자주 사용하는 용어에 대해 바르게 정의해보자!
✉️ JS 런타임을 단순화해보자!
- 구글 V8 엔진을 예시로 들어보자.
- V8은 웹 브라우저를 만드는 데 기반을 제공하는 오픈 소스 자바스크립트 엔진임.

- 엔진은 두 개의 주요 컴포넌트로 구성되어 있음.
- Memory Heap : 메모리 할당이 일어나는 곳.
- Call Stack : 코드의 stack frames 단위로 실행이 일어나는 곳.
- 개발에 필요한 대부분의 JS API가 있음.
- 없는 것도 있음. 이건 뒷 부분에 알게 됨.
📥 The Call Stack
- JS는 싱글 스레드 언어임.
- 하나의 Call Stack을 가지고 있다는 의미이자, 한번에 하나의 코드만을 실행할 수 있다는 뜻.
- Call Stack은 자료구조가 기록되는 곳임.
- 함수를 실행하면, 해당 함수를 stack 최상단에 놓고, 만약 함수로부터 return 값을 받으면, stack에서 해당 함수를 pop함.

이 예시는 어떻게 될까?
- 엔진이 이 코드를 실행하면, 먼저 함수 foo를 실행하게 됨.
- 하지만 foo는 어떤 종료 조건 없이 자기 자신을 호출하고 있음.
- 함수가 실행될 때마다 자기 자신을 Call Stack에 추가하고 또 추가함.

- 브라우저는 Call Stack이 실제로 추가할 수 있는 stack frame을 초과하면 다음과 같은 에러를 던지며 동작을 멈춤.

이렇게 싱글 스레드는 간단하다! 근데.. 한계는 없을까?🤔
⛔ Blocking
- 엄청 난 시간을 필요로 하는 call stack이 올 경우 문제가 됨.
- 예를 들어 API 요청이나 이미지 프로세싱은 느림.
- 해당 코드는 브라우저에서 실행되고 있기 때문에 문제가 되는 것임.
- 이 동작을 동기적으로 처리한다면 다음 함수를 실행할 수 없고. 그동안 브라우저에 모든 동작을 할 수 없게 됨.
- 만약 이를 모르는 사용자가 다른 버튼을 여러 번 클릭한다면 요청이 끝난 후 버튼 이벤트를 순차적으로 실행하며 웹이 정상적으로 동작할 수 있는 상태가 되지 못함.

- 멋지고 유동적인 UI, UX를 생각한다면 stack에 필요없는 느린 코드를 쌓아서 브라우저가 할 일을 못하게 만들지 말자.
❓ 실제론 비동기적으로 동작하던데..
Concurrency & the Event Loop가 가능하게 함.

- 브라우저에서 제공하는 Web APIs가 있음!
- JS에서 호출할 수 있는 스레드를 효과적으로 지원함.
- 앞에서 JS는 대부분의 API를 제공하지만
DOM
,AJAX
,setTimeout
는 해당 되지 않음. 대신 Web API가 제공해주기에 사용할 수 있는 것임(처음 의문에 대한 답).
💡 이벤트 루프가 뭐지?
- Event loop는 Call Stack과 Callback Queue를 지켜보는 일을 함.
- Call Stack이 비어있다면, Event Loop는 Call Stack에 이벤트를 넣음.
- 각 이벤트는 콜백함수가 됨.
방금 다뤘던 예시를 적용해보자!

- state, browser console, call stack이 비워져 있음.
- call stack에
console.log('Hi')
추가.
console.log('Hi')
실행.
- call stack의
console.log('Hi')
삭제.
- call stack에
setTimeout(function cb1() { ... })
추가.
setTimeout(function cb1() { ... })
실행. 브라우저는 web api로부터 타이머를 만들어 카운트 다운을 시작함.
- call stack에
setTimeout(function cb1() { ... })
삭제(완료된 것으로 간주).
- call stack에
console.log('Bye')
추가.
console.log('Bye')
실행.
- call stack에
console.log('Bye')
제거.
- 5초 후, 타이머 종료 후 Callback Queue에 콜백함수인
ch1
추가.
- Event Loop는 Callback Queue로부터
cb1
을 받아 call stack에 추가.
cb1
이 실행되고 call stack에console.log('cb1')
추가.
console.log('cb1')
실행.
- call stack에
console.log('cb1')
삭제.
- call stackd에
console.log('cb1')
삭제.
setTimeout 동작에서 주의할 점!
- setTimeout은 자동으로 event loop가 call stack에 넣어주지 않음.
- callback queue에 넣어짐에 따라 생기는 현상을 살펴보자.
- 설정한 시간 후에 callback queue에 넣어지지만, call stack이 비워져 있지 않거나 앞전에 다른 이벤트가 있다면 해당 콜백함수는 기다려야 함.
스크롤 이벤트도 스크롤 시 callback queue에 많은 이벤트를 쌓음. 유저가 스크롤을 멈출 때까지 작업량을 줄이는 방법도 있음.
참고 자료 :