회고

오늘은 자바스크립트 특징인 비동기 프로그램 실행에 대한 미션이 주어졌고, 비동기적인 동작의 흐름을 학습하였다. 미션 주제는 Call Stack, Event Queue 를 구현해보는 것이었다.

이전에는 비동기가 어느 시점에서 어떻게 동작하는 지를 정확히 인지하지 못한 상태로 개발하며 어려움이 있었고, 특히 Async/await, Promise를 불필요하게 사용하는 경우가 많았다.

자바스크립트는 단일 스레드 기반으로 단 하나의 CallStack 가지며, 스택 프레임을 쌓으면서 동기적으로 코드를 실행시킨다. 단일 스레드로 동작하면, 실행 Context가 비용(IO, ...)이 큰 작업에서 Block되는 단점을 가지고 있다. 이런 단점을 극복하기 위해, 비동기적인 흐름을 가질 수 있도록 브라우저에서 Web API를 제공한다. 결국 Web API를 호출하는 시점부터 비동기적으로 동작하는 것이다.

Web API는 인자로 CallBack 함수를 받고, 비동기적인 실행을 완료하면 Event Queue에 CallBack 함수를 Enqueue 한다. 이후 Call Stack이 빈 상태가 되면, Event Queue에서 하나씩 Pop하여 Call Stack에서 CallBack 함수를 실행시킨다. Event Queue는 Call Stack이 모두 비워질때까지 대기한다. 자세한 것은 [JS] CallStack & EventLoop를 참고하길 바란다.

미션 구현 방식은 Web API setTimeout을 zero delay로 호출하여 Call Stack, Event Queue 번갈아 실행하도록 구성하였고 각각 재귀 호출하도록 구성하였다. 또한, 위에서 말한 동작방식 기반으로 기능을 구현하였다.

function executeEventQueue(callStack, eventQueue) {
    if (callStack.length === 0 && eventQueue.length > 0) {
        callStack.push(eventQueue.shift());
    }

    setTimeout(function () {
        executeEventQueue(callStack, eventQueue);
    }, 0);
}

function executeCallStack(callStack, lastTime) {
    if ((Date.now() - lastTime) > 5000) {
        process.exit(0);
    }

    let next = lastTime;
    while (callStack.length > 0) {
        callStack.pop()();
        next = Date.now();
    }

    setTimeout(function () {
        executeCallStack(callStack, next);
    }, 0);
}

let callStack = [runSync.bind(null, 1), runSync.bind(null, 2)];
let eventQueue = [runAsync.bind(null, 1), runAsync.bind(null, 2), runAsync.bind(null, 3)];

이번 미션으로 Call Stack과 Event Queue를 구현해보며, Javascript 비동기 동작 흐름에 대해 알 수 있었다.