bkdragon's log

[JavaScript] Call Stack, Callback Queue 본문

concept

[JavaScript] Call Stack, Callback Queue

bkdragon 2023. 6. 24. 18:43

Q. 자바스크립트의 특징을 설명하시오.

A. 싱글 스레드 논 블로킹.

 

저 한 문장을 이해하기 위해서는 Call Stack과 Task Queue를 활용한 자바스크립트의 동작 원리를 알 필요가 있다. 용어 정리부터 하자.

 

콜 스택

실행 컨텍스트를 저장하는 자료구조. 실행 컨텍스트는 일반적으로 함수가 실행되면 발생한다.

이를 스택(후입선출) 자료구조에 넣고 순서대로 실행하게 된다.

싱글 스레드

JavaScript는 싱글 스레드를 지원하는 언어이다. 이것은 하나의 콜 스택을 가진다는 것을 의미한다. 콜 스택이 하나이기 때문에 한번에 하나의 일 밖에 처리하지 못한다.

헌데 실제 JavaScript가 구동되는 환경 즉, JavaScript 런타임(브라우저 , node.js)은 멀티 스레드처럼 실행된다.

메크로 테스크 큐

콜백 큐는 두가지 종류가 있다. 메크로 테스크 큐는 비동기 함수가 실행 된 후 콜백 함수가 대기하는 자료구조이다.

마이크로 테스트 큐

마이크로 테스크 큐는 Promise를 통한 비동기 요청 시의 콜백 함수가 대기하는 자료구조이다.

테스크 큐와 동일 계층에 존재한다.

 

자바스크립트는 싱글 스레드를 지원하지만 런타임에서는 멀티스레드처럼 작용한다고 위에서 말했다. 이게 무슨 말일까?

핵심은 Web APIs와 Event Loop, Callback Queue를 통한 비동기 처리이다.

 

어떻게 멀티스레드처럼?

  • Web APIs는 브라우저에서 제공하는 별도의 API이다. Web APIs(백그라운드)가 DOM, AJAX, Timeout과 같은 것들을 처리한다. 비동기 함수를 실행하고 완료되면 비동기 함수의 콜백을 Task Queue로 보낸다.
  • Event Loop에서 Call Stack이 비어 있는지 확인하고 비어있으면 Task Queue 콜백함수들을 Call Stack에 추가한다. (마이크로 테스크 큐가 우선순위를 갖는다.)
  • 결국 모든 동작은 하나의 스레드(싱글 이벤트 루프 스레드)로 동작한다. 하나의 Call Stack에서 처리된다.
export default function TastQueuePage(){

	const onClickTimer = () => {

		console.log("=======시작!!!!=======")

		setTimeout(() => {
			console.log("1000초 뒤에 실행될 거예요!!!")
		}, 1000)

		for(let i=0; i<=9000000000; i+=1){
			sum = sum + 1
		}

		console.log("=======끝!!!!=======")
	}

	return <button onClick={onClickTimer}>시작!!!</button>;
}
  1. 버튼을 클릭하면 onClickTimer가 실행되고 실행 컨텍스트가 Call Stack에 쌓인다.
  2. ‘=======시작!!!!=======’ 출력
  3. setTimeout을 만나면 Web APIs에서 실행하고 1초 뒤 콜백을 Task Queue로 이동시킨다.
  4. 반복문이 실행된다.
  5. ‘=======끝!!!!=======’  출력
  6. Event Loop가 call stack이 빈 것을 확인하고 Task Queue의 콜백 함수를 Call Stack으로 옮긴다.
  7. ‘1000초 뒤에 실행될 거예요!!!’ 출력

 

비동기 처리에 있어 ES6에서는 주로 async / await를 사용한다. 이는 콜 스택과 콜백 큐에 어떤식으로 들어갈까?

async / await

  • async /await는 프로미스 코드를 동기적 코드처럼 다룰수 있게 한다.
  • await는 Promise 앞에서만 쓸 수 있다.
  • await 키워드를 만나면 async 함수의 실행이 일시정지되고 async 함수 전체가 마이크로 태스크 큐로 삽입된다. 이로 인해 Call Stack이 비어있는 상태가 되고 다른 스크립트를 실행하거나 이벤트 처리 등을 할 수 있게 된다.
<html lang="ko">
  <head>
    <title>이벤트루프</title>
    <script>
      function onClickLoop() {
        console.log("=======시작!!!!=======");

        function aaa() {
          console.log("aaa-시작");
          bbb();
          console.log("aaa-끝");
        }

        async function bbb() {
          console.log("bbb-시작");
          await ccc();
          console.log("bbb-끝");
        }

        async function ccc() {
          console.log("ccc-시작");
          const friend = await "철수";
          console.log(friend);
        }

        aaa();

        console.log("=======끝!!!!=======");
      }
    </script>
  </head>
  <body>
    <button onclick="onClickLoop()">시작하기</button>
  </body>
</html>
  1. onClickLoop 함수가 실행되고 실행 컨텍스트가 call stack에 쌓인다.
  2. "=======시작!!!!=======" 출력
  3. aaa 함수가 실행되고 실행 컨텍스트가 call stack에 쌓인다.
  4. "aaa-시작" 출력
  5. bbb 함수가 실행되고 실행 컨텍스트가 call stack에 쌓인다.
  6. "bbb-시작" 출력
  7. ccc 함수가 실행되고 실행 컨텍스트가 call stack에 쌓인다.
  8. "ccc-시작" 출력
  9. ccc 함수의 비동기 작업이 완료되기를 기다리는 동안 ccc 함수의 컨텍스트가 call stack에서 제거된다.
  10. ccc 함수의 비동기 작업이 완료되면 컨텍스트가 매크로 태스크 큐로 이동한다.
  11. "aaa-끝" 출력, call stack에서 aaa 함수의 실행 컨텍스트가 제거된다.
  12. "=======끝!!!!=======" 출력, onClickLoop 함수의 실행 컨텍스트가 제거된다.
  13. call stack에 비어있어서 ccc 함수의 컨텍스트가 다시 call stack으로 이동한다.
  14. 비동기로 받아온 friend 변수가 출력된다. call stack에서 ccc 함수의 실행 컨텍스트가 제거된다.
  15. bbb 함수가 call stack으로 이동한다.
  16. “bbb-끝” 출력 call stack이 빈다.

 

블로킹 / 논 블로킹

블로킹(Blocking)은 자신의 작업을 진행하다가 다른 주체의 작업이 시작되면 자신의 작업을 멈추고 해당 작업을 기다렸다가 다시 자신의 작업을 시작한다.

 

논 블로킹(Non-Blocking)은 다른 주체의 작업에 관련 없이 자신의 작업을 하는 것을 의미한다.

 

setTimeout을 사용했던 코드 진행 과정을 다시 보면 setTimeout과 별개로 현재 콜 스택에 있는 작업을 정상적으로 진행했었다. 즉 자바스크립트는 논 블로킹으로 동작한다.

 

 

이제 자바스크립트의 특징을 이해하고 대답할 수  있을 것 이다.

'concept' 카테고리의 다른 글

Flux 패턴  (0) 2023.07.17
[TEST] MSW와 통합 테스트  (0) 2023.07.12
[TEST] 프론트의 단위 테스트와 BDD  (0) 2023.07.09
[Test] Jest, React-testing-library  (0) 2023.07.07
react-daum-postcode, antd, react-hook-form 조합하기  (0) 2023.03.27