[The Core] JavaScript #2. Intro_ Event Loop

Backgrounds

Single-thread 프로그래밍 언어에서는 복잡한 함수의 실행 또는 너무 많은 함수로 인해 오류가 발생할 수 있다. 이를 해결하고자 Asynchronous 즉, 비동기 함수를 사용한다.

Asynchronous 란, 비동기로 실행되어야 한다고 선언된 함수가 해당 Code Block내의 시간 순서에 따른 동기적(Synchronous) 함수가 모두 실행된 이후에 호출되는 것이다. JavaScript의 대표적인 비동기 함수인 setTimeout을 사용할 경우 어떻게 될까?

위와 같이 작성된 코드의 결과는 아래와 같다. asyncTwo 함수가 비동기로 실행된다.

No. 1
No. 3
No. 2

JavaScript Basic — #2. Event Loop

JavaScript의 Event Loop은 Call Stack과 Callback Queue를 모니터링하는 역할을 담당한다. Call Stack은 지난 포스팅에서 작성한 것과 같이 호출(Call)된 함수가 Stack에 쌓여(push), 실행되면 Stack에서 삭제되는(pop) 자료구조를 의미한다. Callback Queue는 위에서 설명한 것과 같이 비동기로 실행되는 Callback 함수들이 쌓이는 곳 이라고 보면 된다.

Single-thread의 언어가 효율적으로 코드를 실행하는 방법은 간단하지만 중요한 Event Loop의 역할에 달려있다.

Call Stack과 Callback Queue를 모니터링 하며, Call Stack이 완전하게 비어져있을 때, Callback Queue에 먼저 들어온 함수를 다시 Call Stack에 추가하여 비동기 함수를 실행하는 과정을 반복하는 것이다.

위에서 작성한 코드가 어떻게 Call Stack과 Callback Queue에 쌓여 최종적으로 console에 찍히는지는 다음과 같다.

추가적으로, Web API는 브라우저에서 제공하는 JavaScript 함수, 모듈로 DOM, AJAX, setTimeout 등이 있다. setTimeout을 JavaScript 자체에서 자주 사용하기 때문에 Web API에 해당하는 것이 맞는가 라는 의문을 가진 적이 있었지만, mdn에서 검색하였을 때에도 WindowTimers.setTimeout()라는 제목을 갖고 있었고 setTimeout에서의 thiswindow를 의미한다는 것에서 외부 API에 해당한다는 것임을 알 수 있다.

간단하지만 JavaScript 내에서 발생하는 Synchronous, Asynchronous 작동 원리는 위와 동일하게 이루어진다. 함수의 수가 많아질 수록 Call Stack과 Callback Queue는 복잡해질 것이고, 함수의 실행 및 반환 값을 예측하기 어려워질 수도 있다.

JavaScript ES6에서는 비동기를 직접 핸들링할 수 있는 async/await 키워드를 제공해주어 동기/비동기 코드의 작성을 돕고, 복잡하고 nested 되어 있는 callback함수의 늪에서 벗어날 수 있도록 도와주기도 한다.


Wrap-Up

처음 JavaScript 문법을 배울 때, setTimeout, setInterval를 Asynchronous Call의 예시라고 배우고 그 경우의 console에 찍히는 순서를 맞춰보는 공부를 했었다. 하지만 작성하는 코드가 복잡해지고 sever-side 코딩을 하게 되면서 비동기 문제는 매우 어려워졌지만 그만큼 매우 중요한 문제였다.

여러 유용한 debugger들과 chrome console이 있기에 동기/비동기 문제를 완벽하게 핸들링하지 못해도 출력된 결과를 보고 디버깅을 할 수는 있지만, 보다 효율적이고 견고한 코드를 작성하고 확장 가능한 코드를 작성하기 위해서는 기본적인 자바스크립트의 작동 원리를 파악해가는 것이 중요할 것이라 생각한다.