본문 바로가기
Study/Knowledge

[JS] 자바스크립트 동작방식 - 싱글쓰레드, 비동기처리

 

자바스크립트는 기본적으로 싱글쓰레드기반의 비동기처리를 지원한다.

싱글쓰레드는 하나의 잡만을 수행하며, 멀티진행이 안된다는 말이다.

하지만 자바스크립트는 비순차적으로 하나의 이벤트의 대해서 많은 함수라도 동시성을 보장하며  빠르게 처리한다.

이것이 비동기 처리이다.

 

동기적으로 실행된다면, 함수 A안에서 B가 실행될때, 일반적인 A안에서 호출된 B가 끝나고 복귀하여 A가 실행되지만,

자바스크립트는 A는 A대로 호출된 B는 B대로 실행이 된다. 

이 동시성 처리는 UI를 랜더링하고, 이벤트 I/O를 통해 JS를 처리하는 브라우저 엔진에서 호출스택에 담아 처리한다.

 

 

호출스택

웹에서 무엇인가 I/O를 통한 이벤트가 발생하면,  태스크큐에 작업목록으로 올라간다.

이후 큐에 FIFO방식으로 첫번째 작업을 수행하는데,

이 작업에서 first() ->second() ->third() 순서로 함수가 호출된다면,  

호출된 순서대로 차곡차곡 호출스택에 쌓인다.

자바스크립트는. 비동기적이기 때문에, 호출되는 순서대로 스택에 쌓이지만,

실행은 호출되는 스택이 아닌 큐와 같이 순서대로 실행이 진행된다.

이후 호출스택의 함수목록이 비워지면. 태스크큐에 작업이 새롭게 올라온다.

 

여기서 중요한건!  호출된 함수중에서 백그라운드로 작업을 넘기는 함수가 존재한다.

바로 setTimeOut !

이 함수를 이용하여 타이머가 설정되면, 작업은 백그라운드로 실행되고, 호출스택에서 사라진다.

타임아웃이되면 새로운 작업으로 태스크큐에 함수가 쌓이는데,

이를 통해 비동기적으로 실행되는 JS안에서,  순서를 제어할 수 있는 방법으로 쓰인다.

function run() {
  console.log('동작');
}
console.log('시작');
setTimeout(run, 3000);
console.log('끝');

결과:

시작 > 끝 > 동작

 

메인의 시작, 끝이 먼저 돌고, setTimeOut은 백그라운드로 작업이 밀리고, 타임아웃후에야 태스크큐에 진입하는데

이는 호출스택에 함수가 아무리 많아도 비워지고나서, 태스크큐에서 순서를 기다려야하는 상황이 되는것이다.

 

 

예제를 통해서 한번더 점검해보자

 

메인에서

baz()함수를 0.01초 setTimeOut ,

foo()함수를 호출한다면? 결과는 무엇일까? 

function delay() {
    for (var i = 0; i < 100000; i++);
}
function foo() {
    delay();
    bar();
    console.log('foo!'); // (3)
}
function bar() {
    delay();
    console.log('bar!'); // (2)
}
function baz() {
    console.log('baz!'); // (4)
}

setTimeout(baz, 10); // (1)
foo();

 

 

 

 

 

.

.

.

.

.

.

.

 

 

 답은 foo! > bar! > baz! 순서로 출력된다.

 

delay()와 setTimeOut이 비슷해보여도 내부적으로는 동작자체가 완전히 다르다.

delay는 쓰레드를 잡아두기위해 내부적으로 연산을 시킨과정일 뿐이지만 ,

setTimeOut()은 말씀드린대로 백그라운드 실행으로 작업을 전환하기때문에,

호출스택에서 사라지고 태스크큐안에 작업이 밀려들어갑니다.

따라서 setTimeOut이 0초라도 가장 마지막에 실행됩니다.

 

 

거지같은 그림이지만, 스택에 작업이 쌓이고, 비동기적으로 함수 실행이되므로 호출된 순서대로 로그가 찍히며.

백그라운드에서 setTimeOut후에 호출스택이 비워지면, Queue로 작업을 넘기고

Task Queue에서 새로운 작업을 올리는 프로세스 그림입니다.