자바스크립트가 싱글 쓰레드 언어라고 불리는 이유는 메인 쓰레드인 이벤트 루프가 싱글 쓰레드이기 때문에 그렇게 부른다.
언어 자체는 싱글쓰레드지만 웹 브라우저나 Node.js같은 멀티 쓰레드 환경에서 실행되기때문에 런타임은 싱글쓰레드가 아니다.
이 그래프가 간단하게 설명해주고 있다.
동기(sync): 요청이 들어오는 순서에 따라 응답을 받는다. 여러가지 요청을 동시에 처리할 수 없다.
비동기(Async): 하나의 요청을 즉시 처리 하지 않아도, 그 대기 시간동안 다른 요청도 처리가 가능하다.
왜 비동기를 쓰는걸까?
만약 실시간 방송이나, 녹화영상을 보려고 유투브에 접속했다. 인터넷 환경이 좋지 못하면 버퍼링이 걸리게 되는데, 재생될 때 까지 아무것도 못한다면 얼마나 답답할까?
영상을 시청하면서 채팅도 실시간으로 보낼 수 있고, 댓글을 달거나 삭제하고, 다른 영상을 클릭하고 다양한 요청을 수행할 수 있다.
결국 비동기식으로 처리하게 되면 설계가 복잡해진다는 단점이 있지만, 결과가 주어지는 시간이 길어져도 다른 작업을 수행할 수 있어 효율적으로 자원을 사용할 수 있다.
우선 간단한 예로 setTimeOut()을 사례로 들어보자. setTimeout()은 Web API의 한 종류이다. 코드를 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행한다.
function first() {
console.log('first');
}
function second() {
console.log('second');
}
function third() {
console.log('third');
}
first();
setTimeout(second, 1000); // 1000ms, 1초 뒤 second() 출력
third();
위 예제와 같이 first(), third(), second() 순서대로 결과를 반환하게 된다. 왜 이렇게 동작하는지 자바스크립트 런타임 환경에 대해 알아보자.
위의 이미지에서 중요한 요소들을 알아보자
Call Stack : 자바스크립트에서 수행해야 할 함수들을 순차적으로 스택에 담아 처리
Web API : 웹 브라우저에서 제공하는 API로 AJAX
나 setTimeout
등의 비동기 작업을 실행
Task Queue : allback Queue
라고도 하며 Web API에서 넘겨받은 Callback 함수
를 저장
Event Loop : 단순히 하나의 임무를 수행하는데, all Stack
이 비어있다면 Task Queue
의 작업을 Call Stack
으로 옮긴다. 이러한 반복을 이벤트 루프에서는 틱(tick)
이라고 부른다.
이제 위의 코드를 실행하게되면 다음과 같은 과정을 거친다
가장 처음은 깨끗하게 비워져있다. 브라우저 콘솔, 콜스택 모두 깨끗한상태
first
가 콜스택에 추가된다. 그 다음 실행된다.
콜 스택에서 first
가 제거된다.
setTimeout(second, 1000)
이 콜스택에 추가되고, 실행된다. 브라우저의 웹 API가 가지고있는 타이머를 생성하여 카운트다운을 준비한다.
setTimeout(second, 1000)
실행이 완료되면 콜스택에서 제거된다.
third
콜스택에 추가되고, 실행된다.
third
가 콜스택에서 제거된다.
1000ms, 1초가 지난 다음에 콜백 큐에 넣는다.
이벤트 루프는 second
를 콜백 큐에서 콜스택에 밀어넣는다.
second
가 실행되고 console.log('second')
를 콜스택에 추가한다.
위의 예제에서 setTimeout()은 타이머를 설정하는 비동기 함수로, 타이머가 만료되면 이벤트 루프나 큐에 넣지않는다. 호스팅 환경이 이벤트 루프에 위치시켜 나중에 틱(tick)
이 가져다가 수행하도록 한다.
second
는 1초(1000ms
) 이후 콜백 큐에 추가되며, 만약 큐에 먼저 추가된 이벤트들이 존재할 경우 second
가 실제로 실행되기 전 다른 이벤트들이 끝날 때까지 기다려야 할 수도있다.
요약
ref: 이벤트 루프와 비동기, 캡틴판교, 자바스크립트는 왜 싱글 쓰레드일까?