프론트엔드 개발자로 일하기

자바스크립트 호출스택(call stack), 이벤트 루프

프론트루나 2024. 9. 19. 10:05
반응형

 

호출스택은 여러 함수들을 호출하는 스크립트에서 해당위치를 추적하는 인터프리터를 위한 메커니즘이다.
현재 어떤 함수가 실행중인지, 그 함수 내에서 어떤 함수가 호출되어야 하는지, 등을 제어합니다.

  • 스크립트가 함수를 호출하면, 인터프리터는 이를 호출 스택에 추가한 다음 함수를 실행하기 시작한다.
  • 해당함수에 의해 호출되는 모든 함수는 호출 스택에 추가되고, 호출이 도달하는 위치에서 실행한다.
  • 현재 함수가 끝나면, 인터프리터는 스택을 제거하고 호출스택 마지막 코드 목록에서 중단된 실행을 다시 시작한다.
  • 스택이 할당된 공간보다 많은 공간을 차지하면 "stack overflow" 에러가 발생한다.
function greeting(){
    sayHI();
}
function sayHI(){
    return "Hi!";
}

greeting();
더보기

greeting() 함수에 도달할 때까지, 모든 함수를 무시합니다.
greeting() 함수를 호출 스택 리스트에 추가합니다.
참고: 호출 스택 리스트. - greeting

greeting 함수 내부의 모든 코드를 실행합니다.
sayHi() 함수를 호출합니다.
sayHi() 함수를 호출 스택 리스트에 추가합니다.
참고: 호출 스택 리스트. - sayHi - greeting

sayHi() 함수의 끝에 도달할 때까지, 함수 내부의 모든 코드를 실행합니다.
sayHi() 가 호출된 라인으로 돌아와 greeting() 함수의 나머지를 계속 실행합니다.
호출 스택 리스트에서 sayHi() 함수를 제거합니다.
참고: 호출 스택 리스트. - greeting

greeting() 함수 내부의 모든 코드가 실행되었을 때, 이를 호출한 라인으로 돌아와 JS 코드의 나머지를 계속 실행합니다.
호출 스택 리스트에서 greeting() 함수를 제거합니다.
참고: 호출 스택 리스트. EMPTY

요약하면, 빈 호출 스택으로 시작하여, 함수를 호출할때마다 자동으로 호출 스택에 추가된다.
해당 코드가 모두 실행된 후 호출 스택에서 자동으로 제거된다.
마찬가지로 빈 호출 스택으로 끝난다.


이벤트 루프(event Loop)

싱글 스레드의 한계를 보완하기 위해 비동기콜백(asynchronous callback)을 이용한다.
비동기 콜백에는 web APIs, ES6의 Promise가 있다.

자바스크립트 코드 실행중에 이벤트를 만나면, 이벤트가 콜백 큐에 차례대로 쌓인다.
콜백 큐는 FIFO 선입선출 룰을 따른다.

이벤트 루프는 콜스택이 비었는지 확인하고, 비었으면 콜백 큐에 있는 이벤트를 가져다 콜스택에 밀어넣는다.
이 한번의 작업을 틱(tick)이라고 한다. 이벤트 루프는 이 작업을 반복한다.

Heap
객체는 힙에 할당된다. 힙은 단순히 메모리의 큰 영역을 지칭하는 용어입니다.

Queue
Javascript 런타임은 메세지큐, 즉 처리할 메세지의 대기열을 사용합니다.
각각의 메세지에는 메세지를 처리하기 위한 함수가 연결되어 있습니다.

이벤트 루프의 임의 시점에, 런타임은 대기열에서 가장 오래된 메세지부터 큐에서 꺼내 처리하기 시작합니다.
이를 위해 런타임은 꺼낸 메세지를 매개변수로, 메세지에 연결된 함수를 호출합니다.
함수를 호출하면 해당 함수가 사용할 새로운 스택 프레임이 생성됩니다.

함수처리는 스택이 다시 텅 빌때까지 계속죕니다. 그 후, 큐에 메세지가 남아있으면 같은 방법으로 처리를 계속 진행합니다.

while(queue.waitForMessage()){
    queue.processNextMessage(); // 큐가 텅 비어있을때까지 계속 실행
}

 

Run-to-completion

각 메세지의 처리는 다른 메세지의 처리를 시작하기 전에 완전히 끝납니다. 
이 특징은 프로그램의 동작을 추론할때 유용한 특성을 제공합니다. 실행한 함수가 다른 작업에 의해 선점될 일이 없고, 다른 모든 코드의 실행보다 우선해서 값을 변경할 수 있으며, 중단되는 일 없이 완전히 끝나기 때문입니다. 
반면, 예를들어 C언어의 경우, 스레드에서 실행중인 함수를 런타임 시스템이 임의로 멈추고 다른 스레드의 코드를 먼저 실행할 수 있습니다. 

이 모델의 단점은, 만약 메세지를 처리할때 너무 오래 걸리면 웹 애플리케이션이 클릭이나 스크롤 같은 사용자 상호작용을 처리할 수 없다는 점입니다. 브라우저는 "스크립트 응답 없음" 대화상자를 표시해서 이 문제를 완화합니다. 
개발자로서 사용할 수 있는 좋은 방법으로는 메세지 처리를 가볍게 유지하고, 가능하다면 하나의 메세지를 여러개로 나누는 것입니다. 

메시지 추가하기

웹 브라우저에서는, 수신기가 부착된 이벤트가 발생하면 새로운 메시지가 추가됩니다. 수신기가 없으면, 이벤트는 유실됩니다. 즉, 클릭 이벤트 처리기가 붙은 요소를 클릭하면 메시지가 새로 추가되는 식입니다. 다른 이벤트에 대해서도 마찬가지입니다.

 

setTimeout 함수는 두 개의 매개변수를 가집니다. 첫 번째는 큐에 추가할 메시지, 두 번째는 시간 값(선택 사항, 기본 값 0)입니다. 시간 값은 메시지를 큐에 추가하기까지 기다릴 (최소) 지연 시간을 나타냅니다. 큐에 다른 메시지가 없고 스택이 비어있다면 setTimeout의 메시지는 딜레이 직후 즉시 처리됩니다. 그러나, 다른 메시지가 존재한다면 setTimeout은 앞선 메시지의 처리를 기다려야 합니다. 그래서 두 번째 값은 정확한 지연시간이 아닌, '최소' 지연 시간만 나타냅니다.

 

0의 지연 시간

0의 지연 시간을 지정하는 것이 콜백을 0밀리초 후에 호출한다는 뜻은 아닙니다. setTimeout의 지연 시간에 0 밀리초를 지정하고 호출하더라도 콜백 함수는 즉시 실행되지 않습니다.

실제 실행 시점은 큐에서 대기 중인 작업의 수에 따라 다릅니다. 아래 예제에서는 '평범한 메시지'가 콜백의 호출보다 앞서 콘솔에 기록될 것입니다. 지연 시간은 요청을 처리하기 전에 대기할 '최소' 시간이고, 보장 시간이 아니기 때문입니다.

 

setTimeout에 특정 지연 시간을 지정하더라도, 큐에서 대기 중인 모든 메시지의 처리는 기다려야 합니다.


자바스크립트는 논블로킹 비동기 동적 언어이다.

논 블로킹

다른 많은 언어와 달리 JavaScript는 절대 블로킹 연산을 하지 않습니다. 논 블로킹은 이벤트 루프 모델의 무척 흥미로운 특징으로, 대부분의 입출력 처리가 이벤트와 콜백을 통해 수행되므로, 애플리케이션이 IndexedDB 질의나 XHR 요청의 반환을 대기 중이더라도, 여전히 사용자 입력 등 다른 것들을 처리할 수 있는 것입니다.

alert이나 동기적 XHR과 같은 레거시 예외가 존재하긴 하지만 사용하지 않는 것이 좋습니다. 물론 예외에 대한 예외는 조심하세요(그러나 이런 예외는 구현체의 버그인 경우가 많습니다).

 

해당 영상을 보고 많은 도움이 되었다. 

https://youtu.be/8aGhZQkoFbQ?si=7Rm-bXAwVU7VKd_3

 

 

 

 

https://cge96.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EC%8A%A4%ED%83%9D-%ED%81%90-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0stack-queue

 

자바스크립트로 스택, 큐 구현하기(stack, queue)

코테에서 기본으로 등장하는 스택, 큐 를 구현해보자!  큐(Queue)- 데이터를 집어넣을 수 있는 선형(linear) 자료형- 먼저 집어넣은 데이터가 먼저 나온다. (FIFO)- 데이터를 집어넣는 enqueue, 데이터를

cge96.tistory.com

 

반응형