"어떻게 자바스크립트는 싱글스레드이면서 비동기인 걸까?"에 대한 짧은 답은 자바스크립트 언어는 싱글스레드이고 비동기 행위들은 엄밀히 말하면 자바스크립트 언어 그 자체의 일부는 아닙니다. 오히려 비동기 행위는 브라우저 내부(혹은 프로그래밍 환경)에 존재하는 자바스크립트 언어의 핵심 단의 상위에 만들어져있죠. 그리고 브라우저의 API를 통해 접근합니다.
브라우저 내부의 주요 컴포넌트들에 대한 개요
사실 이것들은 자바스크립트 언어 그 자체는 아니고 오히려 자바스크립트 언어 코어 단의 상위에 만들어져, 우리가 자바스크립트 코드 사용 시에 추가적인 초능력을 제공합니다.
예를 들면 Geolocation API는 지역 데이터를 가져오기 위한 간단한 자바스크립트 구조를 제공합니다. 그래서 구글맵에 위치를 표기할 수 있습니다. 백그라운드에서 브라우저는 디바이스의 GPS 하드웨어와 통신하기 위해 사실 C++ 같은 복잡한 로우레벨 코드를 사용하는 중입니다. 위치 데이터를 불러오고 코드에서 사용할 수 있도록 브라우저 환경에 이것을 반환합니다. 하지만 이러한 복잡성은 다시 API에 의해 추상화되어 있습니다.function main() {
console.log('A');
setTimeout(
function display() { console.log('B'); }
, 0);
console.log('C');
}
main();
// A
// C
// B
우리는 'A'와 'C'를 콘솔에 로깅하는 2개의 console.log
를 가진 main 함수를 갖고 있습니다. 그리고 그 사이에 'B'를 콘솔에 로깅하는 0ms의 딜레이를 가진 setTimeout 함수를 호출합니다.
console.log('A')
)를 stack에 넣습니다. 이 statement가 실행되고 완료되자마자 해당 프레임은 스택에서 pop됩니다. 알파벳 A가 콘솔에 표기됩니다.display()
과 함께 0ms의 딜레이를 가진 setTimeout()
)가 콜스택으로 push되고 실행됩니다. setTimeout 함수는 제공된 콜백을 딜레이하기 위해 브라우저 API를 사용합니다. 일단 타이머를 돌리기 위해 콜백이 브라우저로 넘어가면 setTimeout()
을 가진 프레임은 pop됩니다.display()
실행을 위한 타이머가 돌아가는 도중에 console.lg('C')가 콜스택에 push 됩니다. 이러한 경우에는 제공된 딜레이는 0ms 였기 때문에, 콜백은 브라우저가 콜백을 받자마자 메시지 큐에 바로 추가됩니다.setTimeout()
의 딜레이가 0초였음에도 불구하고, display()
으로의 콜백이 콜스택이 존재했던 모든 프레임이 실행될 때까지 기다려야 했던 이유입니다.그래서 setTimeout(function, delayTime)에 들어가는 delay프로퍼티는 어떤 함수가 실행된 뒤의 정확한 시간 딜레이를 말하는 것이 아닙니다. delay 파라미터는 함수가 실행됐을 때의 어떤 지점이후의 최소 대기시간을 의미하는 것입니다.
function main() {
console.log('A');
setTimeout(
function exec() {console.log('B'); }
, 0);
runWhileLoopForNSeconds(3);
console.log('C');
}
main();
function runWhileLoopForNSeconds(sec) {
let start = Date.now(), now = start;
while (now - start < (sec * 1000)) {
now = Date.now();
}
}
runWhileLoopForNSeconds()
는 정확히 이름과 같은 일을 합니다. 함수가 호출된 시간에서 경과된 시간을 계속 측정하여 경과된 시간이 함수의 인자로 받은 시간과 일치하는지 계속해서 검증합니다. 기억해야 할 메인포인트는 while 반복문이 콜스택에서 상주하면서 브라우저 API를 사용하지 못하게 하는 blocking statement라는 것입니다. 이 함수는 실행이 끝날 때까지 뒤에 오는 모든 statements 들이 실행되지 못하게 막습니다.