개발/Javascript

[javascript] 자바스크립트는 싱글 스레드인데, 어떻게 비동기적으로 작동할까?

2realzoo 2022. 11. 28. 12:21

자바스크립트는 싱글 스레드이다. 

그 말인 즉슨, 한번에 한 개의 일만 처리할 수 있다는 의미이다.

실제로도 자바스크립트 코드를 작성하다보면 위에서부터 아래로 쭉 훑고 내려오며 

위에서 에러가 생기면 아래 코드까지는 읽지도 않는다는 느낌을 받을 수 있다.

위 그림은 자바스크립트 엔진이 어떻게 구성되어 있는지를 보여준다.

Call Stack이 한개이기 때문에 자바스크립트를 싱글 스레드라고 부른다. 

 

그런데 이런 자바스크립트에서 비동기라니.

혼자 공부를 하며 이해가 되지 않았다.

스스로 찾아보며 생각한 결론은 '자바스크립트는 동기적이다.' 라는 것이었다.

그럼 setTimeout()과 같은 대표적인 비동기 함수는 어떻게 된걸까?

자바스크립트는 동기적이지만, 자바스크립트 런타임들은 비동기적이다. 

그리고 그 런타임들은 자바스크립트가 일하는 동안 또 일할 수 있다.

자바스크립트에 스레드가 더 생긴 셈이다.

런타임은 이벤트 루프를 통해 비동기적으로 처리한 일을 스택에 넣는다.

그렇다면 이벤트 루프는 어떻게 작동되는가?

JS 엔진은 stack에 쌓인 일을 완료해야 다음 일을 하는데,

위의 그림과 같이 여러 개가 쌓여있는 경우는 함수 안의 함수를 처리하는 경우이다.

아래 예시를 보자.

어떤 순서로 함수가 실행될 지 보면,

(1) printSquare(4) 실행

(2) printSquare(4) 안의 square(4) 실행

(3) square(4) 안의 mutiply(4, 4) 실행

(4) printSquare(4) 안의 console.log(squared) 실행

 

이러한 일의 순서는 stack에 쌓이고 가장 최근에 쌓인 순서대로 실행되고 제거된다.

자바스크립트의 이벤트 루프는 stack에 아무 것도 없고, callback queue에 일이 있을 때 그것들을 스택에 옮기는 일을 한다.

 

우리가 보는 비동기의 대표적 예시인 setTimeout은 webAPIs에 속해있다.

setTimeout은 HTML DOM API이기 때문에 Javascript 안에 속해있지 않다.

그래서 Javascript와 별개로 비동기적으로 작동한다. 

아래의 예시는 setTimeout()이 어떻게 작동되는지 보여준다.

먼저 main()이 stack에 쌓이고, 그 뒤에 첫 코드인 console.log('Hi') 가 쌓였다.

일이 완료되면 console 창에 Hi가 찍히면서 stack의 console.log('Hi')가 제거된다.

그 다음으로 내려가면 setTimeout()을 stack에 넣는다.

그리고 실행되면 자바스크립트 외부(Web API)로 전달되어 타이머가 시작된다.

동시에 stack의 setTimeout()은 제거된다.

스택에서 제거된 setTimeout()

Web API에서 타이머가 실행되는 동안, console.log('SJS') 가 stack에 쌓이고, 실행된다.

마찬가지로 실행 후엔 제거된다.

console창에는 SJS가 찍힌다.

입력한 시간이 지나고 나면 Web API는 Task queue에 이를 전달한다.

event loop는  stack에 아무것도 없으므로 큐에 있는 일을 스택에 쌓는다.

그러고 나면 console.log('there')까지 실행되며 모든 코드가 끝난다.

 


그래서 결국 자바스크립트 그 자체는 비동기라고 할 수 없고, 다른 런타임 덕분에 비동기적으로 보이는 것이었다.