Me/FrontEnd

[JavaScript] JS가 싱글스레드임에도 비동기 처리가 가능한 이유

폴스업데이트 2022. 5. 19. 00:26
728x90

  자바스크립트는 싱글 스레드 언어입니다. 스레드가 하나라면 동시에 하나의 작업만 할 수 있다는 뜻입니다. 어떻게 비동기처리가 가능할까요? 또, 자바스크립트로 쓰여진 웹사이트들은 버튼 클릭, 애니메이션 실행, HTTP 요청 전송 등 여러가지 일을 한번에 처리합니다. 스레드가 하나임에도 이런 일이 가능한 이유를 알아보고자 하였습니다.


  스레드가 하나임에도 동시성이 지원되는 이유를 알기 위해서는 '이벤트 루프'에 대해 알아합니다. 이벤트 루프는 무엇일까요? 

 

  이벤트 루프는 사실 자바스크립트 엔진이 담당하는게 아닙니다. 브라우저에서 담당하는 것이죠. (Node.js던가)

비동기처리를 위한 setTimeout 같은 함수는 Web APIs에서 정의가 되어있습니다. 뭔진 모르겠지만 Callback들도 Task Queue에 들어있네요! 

  자바스크립트 엔진의 Call Stack은 싱글 스레드가 맞습니다. 다만, 자바스크립트가 구동되고 돌아가는 환경, 브라우저는 멀티 스레드를 사용하며 이 환경과 엔진이 서로 잘 맞물려 돌아가게 해주는 것이 바로 이벤트 루프인 것이죠. 


const foo = () => {
	console.log("foo");
}

const bar = () => {
	console.log("bar");
}

setTimeout(bar, 10);
foo();

  위와 같은 코드를 실행하면 무엇이 먼저 출력될까요? foo가 먼저 출력되고, 10ms 뒤에 bar가 출력될 것입니다. foo가 실행되는데 아주아주 오랜 시간이 걸려도, 10ms가 넘게 걸려도 bar는 foo 다음에 출력될 것입니다. 즉, setTimeout 함수로 넘긴 콜백함수는 설정한 시간이 끝나도 현재 실행중인 foo 함수가 끝나기 전까진 대기를 타다 foo가 끝나면 바로 실행되게 되는 것입니다. 이 일련의 과정은 위의 이벤트 루프에 의해 실행되는데요!

 

Task Queue는 콜백 함수들이 대기를 하고 있는 queue입니다. 이벤트 루프는 자바스크립트 엔진(싱글 스레드)의 Call Stack이 비워질때마다 Tasks queue에서 함수 하나를 꺼내와 실행을 해주는 역할을 합니다. 즉, setTimeout함수에서 10ms가 지나면 바로 Call Stack에 넣어 실행하는 것이 아니라 Task queue라는 곳에 집어넣는 것이죠. 이 bar함수는 이벤트 루프가 꺼내줄 때까지, 즉 Call stack의 태스크 실행이 끝날때까지 대기를 타다가 foo의 함수가 끝나면 이벤트 루프에 의해 Call Stack으로 꺼내져 와 실행이 되는 것입니다.

 

  이벤트 루프를 간단한 의사코드로 나타내면 다음과 같아요.

while(queue.waitForMessage()){
	queue.processNextMessage();
}

'현재 실행중인 태스크가 있는지' 확인하고, 'Task Queue에 남은 함수가 있는지' 확인하는 루프가 있기 때문이죠. 그래서 이벤트 '루프'라는 이름이 붙은게 아닌가 싶어요.

 

  모든 비동기 API는 작업 완료시 싱글 스레드에 우겨넣는 것이 아니라, Task Queue에 추가하고 이를 이벤트 루프가 현재 Call Stack이 비워지면 순차적으로 추가한다!

 

가 글 제목에 대한 답변이 되겠습니다. 이는 setTimeout에서 시간 인자 값이 0일때도 예외는 아닙니다.

 


참고 문헌

https://meetup.toast.com/posts/89

728x90