본문 바로가기

JavaScript

자바스크립트 동작 원리(Call Stack, Task Queue, Event Loop etc)

코드를 잘 짜고 싶으면 문법말고도 여러가지 배울것이 많다. ex: 웹브라우저 동작 원리 등.

그러면 코드 잘짜는 개발자가 될 수 있다.

 

자바스크립트를 작성하면 브라우저가 실행시킨다. HTML, CSS, JS를 해석해주는 것이 브라우저.

- 특징 : 자바스크립트를 해석하는 순서가 특이함.

 

아래 코드를 실행시키고싶다.

1+1 하고

1초 쉬고

2+2 하고

 

일반 프로그래밍 언어는 이런식을 작동한다.

//python ver
print(1+1)
time.sleep(1)
print(2+2)

파이썬 같은 경우는 위에서부터 한줄한줄 실행된다.

2 → 1초 쉬고 4

 

 

자바스크립트에서도 똑같은 기능을 만들어보려고 setTimeout을 사용해서 코드를 작성하였다.

//JavaScript ver
console.log(1+1)
setTimeout(function(){}, 1000) //1000ms 쉬어라
console.log(2+2)

하지만 1초 쉬지도 않고 바로 2 4 이렇게 출력한다. 이건 setTimeout 사용방식을 틀리게 적어서 그렇다.

 

//JavaScript ver
console.log(1+1)
setTimeout(function(){console.log(2+2)}, 1000)

이렇게 해야 2 1초 쉬고 4가 나온다.

 

 

추가로 console.log(3+3)을 적었더니..!

console.log(1+1) //1. 2 출력
setTimeout(function(){console.log(2+2)}, 1000) //3. 1초 후에 4 출력
console.log(3+3) //2. 6 출력

 

2 출력 6 출력 1초 쉬고  4 출력

보통 프로그래밍 언어와 동작방식이 다르다.

뭔가 처리가 빨리 되는것부터 빨리 실행하는 느낌?그렇다고 자바스크립트가 병렬처리가 된다, 라고 생각하면 안된다. 

자바스크립트 동작 원리를 알아야한다.

 

* 자바스크립트 엔진 : 자바스크립트 코드를 실행하는 프로그램 또는 인터프리터

웹 브라우저에 자바스크립트 엔진이 내장되어있다. 

 

 

 

자바스크립트 엔진이 구동되면서 실행중인 코드를 추적하는 공간이 콜스택.

(콜스택은 프로세스의 스택영역 안에 존재하는 자료구조!)

 

# Call Stack(실행 컨텍스트 스택)

: 코드의 실행순서를 관리

2023.12.08 - [JavaScript/모던 자바스크립트 Deep Dive] - 실행 컨텍스트 스택

 

실행 컨텍스트 스택

const x = 1; function foo() { const y = 2; function bar() { const z = 3; console.log(x + y + z); }; bar(); }; foo(); 위 예제는 소스코드의 타입으로 분류할 때, 전역코드와 함수 코드로 이루어져 있다. 자바스크립트 엔진

lion284.tistory.com

 

여기에 코드를 한줄한줄 집어넣고 한줄한줄 실행해준다. 웹브라우저 내부 코드실행장소. 

console.log(1+1) 실행 console.log(2+2) 실행

 

 

코드를 실행할 때 변수를 만나면, 옆에 heap이라는 공간에 변수가 저장된다.

 

자바스크립트 엔진은 메모리 영역에 콜스택이 하나밖에 없다. 그래서 한번에 하나의 코드만 실행할 수 있다.

싱글 스레드

단 하나의 콜 스택을 사용하기 때문에 최상위 실행 컨텍스트(실행중인 실행 컨텍스트)가 종료되어 콜스택에서 제거되기 전까지는 다른 어떤 태스크도 실행되지 않는다.

 그래서 '자바스크립트는 싱글 스레드'이다. 그래서 원래는 자바스크립트는 병렬처리가 안된다

 

단순히 태스크가 요청되면 콜스택을 통해 요청된 작업을 순차적으로 실행할 뿐이다.

 

 

# Heap

객체가 저장되는 메모리.

콜 스택의 요소인 실행 컨텍스트는 heap에 저장된 객체를 참조한다.

메모리에 값을 저장하려면 먼저 값을 저장할 메모리 공간의 크기를 결정해야 한다. 객체는 원시 값과는 달리 크기가 정해져있지않으므로 할당해야 할 메모리 공간의 크기를 런타임에 결정해야한다. 

(사실 원시 값도 객체인 실행 컨텍스트에 저장되므로 자바스크립트의 모든 값은 객체로 heap에 저장된다고 할 수 있다.)

따라서 객체가 저장되는 메모리공간인 heap은 구조화되어있지 않다는 특징이 있다.

 

 

# 비동기처리를 위한 브라우저 환경

비동기 처리를 위해 브라우저 환경(자바스크립트 런타임 환경)은 Task QueueEvent Loop를 제공한다.

 

# Task Queue

비동기 함수의 콜백함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역.

 

콜스택에 코드를 추가할때 setTimeout같은 코드(비동기함수)는 바로 실행을 할 수 없는 코드이다. 이런건 stack에 넣어서 처리하지 않는다. 

 

이런 비동기함수는 stack에 넣어서 실행하지 않고, 대기실에 잠깐 둔다.

이렇게 잠시 옆에 두는 코드들은 정해져있다.

ex) AJAX 요청 코드, 이벤트핸들러, setTimeout, 비동기함수의 콜백함수 등)

대기실에 두는 코드들은 자바스크립트 엔진이 처리하지 않고, Web API가 처리한다.

더보기

**참고!!

콜스택(Call Stack, 실행 컨텍스트 스택)은 후입선출(Last-In-First-Out)구조이므로 아래부터 차곡차곡 쌓이고 제일 위 실행컨텍스트가 먼저 pop된다.

console.log(1+1), console.log(3+3)은 stack에 push되자마자 바로 pop되므로 구조상 FIFO같이 보이지만 실제로는 LIFO이고, 이해를 돕기위해 console.log(1+1)이 console.log(3+3)보다 위에 있는 것처럼 표현하신 것 같다...!

 

setTimeout함수가 1초가 다 되면Call Stack으로 옮겨와야한다.

하지만, 그냥 옮겨오지 않고 Task Queue에 처리가 완료된 코드들을 줄을 세운다.

이 대기줄에서 하나씩 stack에 올린다.

하지만 여기에는 조건이 있다. stack이 텅 비었을때만 올려보낼 수 있다

 

그래서 이 경우는,

1. console.log(1+1) 실행

2. setTimeout함수는 바로 실행할 수없기때문에 대기실로 잠깐 둔다.

3. console.log(3+3) 실행

4. 대기실에 있던 setTimeout함수는 1초 후 Task Queue에 옮겨져서 Call stack으로 옮겨진다. 

 

 

그럼 아래와 같은 경우는 어떻게 될까?

setTimeout함수에 대기시간이 0초인 경우는?

이 경우 2, 4, 6 이렇게 출력될 것 같지만 그렇지 않다.

 

1. console.log(1+1) 실행

2. setTimeout함수는 무조건 대기실로 잠깐 둔다.

3. console.log(3+3) 실행

4. 대기실에 있던 setTimeout함수는 Task Queue에 옮겨져서 stack으로 옮겨진다. 

 

 

# Event Loop

이벤트 루프는 Call Stack에 현재 진행중인 실행 컨텍스트가 있는지,

Task Queue에 대기중인 함수(콜백함수, 이벤트 핸들러 등)가 있는지 반복해서 확인한다.

만약 콜 스택이 비어있고 태스트 큐에 대기중인 함수가 있다면 이벤트 루프는 순차적으로(FIFO) 태스트 큐에 대기중인 함수를 콜 스택으로 이동시킨다. 이때 콜 스택으로 이동한 함수는 실행된다.

즉, 태스크 큐에 일시보관된 함수들은 비동기 처리 방식으로 동작한다. 

 

 

반복문을 천만번 돌리면 오래걸린다. (그런건 자바스크립트로 짜면 안된다..)

만약 콜 스택에서 10초이상 걸리는 어려운 연산이 있다고 가정해보자.

10초 걸리는 중간에 "버튼클릭, ajax 요청, setTimeout 타이머"를 실행하면 작동이 안된다. 

 

ex: 버튼 누르면 모달창 띄워준다. 이 버튼 누르는것도 이벤트리스너.

대기실을 거쳐서 stack에 올려보내진다. 하지만 stack이 비었을때만 올려보내진다.

10초동안 콜스택이 비워지지 않으면 콜스택으로 올려보낼 수가 없다. 그래서 버튼을 눌러도 아무런 반응이 없다. 

그러면 응답없는 페이지 메시지가 이렇게 뜬다. 

 

자바스크립트는 동기적? 비동기적?

자바스크립트는 원래는 동기적으로 처리된다. 한번에 한 줄 코드가 실행된다.(싱글스레드)

스택에서 한번에 한줄만 코드가 실행되므로. 

하지만 이렇게 브라우저나 Node.js와 같은 런타임 환경이 제공하는 event loop, Web API, Task Queue와 같은 메커니즘 덕분에 비동기적 처리도 가능하다.

 

 

**모든 그림 및 글의 출처는 '코딩애플', '모던자바스크립트 Deep Dive 책'입니다.
https://www.youtube.com/watch?v=v67LloZ1ieI&t=12s