Javascript 동작원리와 Eventloop

Radin·2023년 7월 15일
16

Javascript

목록 보기
1/7

들어가기

💡Javasciprt 동작원리와 이벤트 루프를 알아보기 전 몇가지 질문을 해봅니다.
1. Javascript은 어떻게 동작하는 언어 인가요?
2. 싱글스레드 기반인 언어인데 느리진 않을까요?
3. 동시성을 어떻게 가진걸까요? Javascript의 특징인가요?

개요

Javascript은 싱글쓰레드 언어이다, non blocking 방식이다, 동시성을 가진다 등 관련된 여러 주제 들에 관하여 how? why? 를 고민하며 아티클을 참고하여 정리한 글입니다.

1. 🐬Javascript 동작원리

1-1 🤔Javascript 변수들은 어디에 저장이 될까?

💡Javascript의 데이터 타입은 크게 두가지로 나누어 진다.
1. Primitive(원시) type - Number, String, Boolean, null, undefined, Symbol
2. Reference(참조) type - Array, Function, Date, RegExp, Map, WeakMap, Set, WeakSet

위 그림처럼 원시(기본) 타입은 Call Stack에 저장이 되고 참조(객체)타입은 Memory Heap에 저장되는걸 확인 할 수 있다.

1-2 🤔Javascript는 어떻게 실행이 될까?

💡Call Stack은 원시타입을 저장할 뿐만 아니라 실행 콘텍스트(Execution Context)를 통해 현재 어떤 함수가 동작하고 있는지, 그 함수 내에서 어떤 함수가 동작하는지, 다음에 어떤 함수가 호출되어야하는지 등을 제어 한다.

아래의 코드를 보며 Call Stack이 어떻게 동작할지 생각해 보자

function multiply(a, b) {
	return a * b;
}

function square(n) {
    return multiply(n, n)
}

function printSquare(x) {
	let n = square(x);
	console.log(n);
}

printSquare(5);

위 사진과 같이 실행순서에 따라 스택이 쌓이고 실행이 끝나면 스택에서 사라지는 방식으로 실행이 된다.

🤔 Q 혹시 실행이 계속 쌓이게 되어있는 재귀함수를 실행하면 어떻게 동작할까?

아래의 코드를 예로 들어 콜 스택이 어떻게 동작하는지 확인해 보자

function foo() {
    return foo()
}
foo();

호출 스택(Call Stack)의 최대 허용치를 넘게 되어 Uncaught RangeError: Maximum call stack size exceeded 에러가 나온다.

1-3 🙊Javascript의 blocking

💡 blocking에 대한 정확한 정의는 없지만 “느리게 동작하는 코드” 로 설명 할 수 있다. 즉, 콜 스택에 현재 느리게 동작하는 작업이 남아 있는 것을 말한다. ex) while문안의 수십억개의 동작문, 네트워크 요청, 이미지 프로세싱 등

만약 10초가 걸리는 네트워크 요청 network() 함수가 있다고 가정한다면 아래의 코드는 어떻게 동작할까

network()
console.log(1)
network()
console.log(2)
network()
console.log(3)

💡자바 스크립트는 싱글 스레드 이기 때문에, 하나의 콜 스택을 가지며 한 번에 하나의 일(Task)만 처리 할 수 있다.

network() 호출 -> 10초 딜레이 -> 1출력 -> network() 호출 -> 10초 딜레이 -> 2출력 -> network() 호출 -> 10초딜레이 -> 3출력
이와 같이 특정 코드가 오래 걸려 다른 코드를 실행하지 못하게 되어 block이 된 상태라고 설명 할 수 있다.

생각만해도 너무 답답한 상황이다. 어떻게 block현상을 타개할수 있을까?

이 때 효과적으로 event를 관리하기 위해 필요한 것이 바로 Web APICallback Queue, event loop이다.

2. 🐬이벤트 루프(Event Loop)

2-1. Web API와 Callback Queue

💡지금까지 배운 콜 스택의 개념을 활용하면 콜 스택이 모두 실행 할 것 같지만, JS엔진에는 setTimeout, ajax, EventListner 등 함수가 없기 때문에 웹 브라우저가 대신 실행해주어야 한다. 여기서 바로 “동시성” 개념이 나온다. JS가 싱글 쓰레드 기반임에도 불구하고 동시성 언어라고 부르는 이유는 웹 브라우저가 제공하는 Web API를 통해 동시에 작업을 할 수 있기 때문이다.
즉, 자바스크립트 자체는 싱글 스레드 이지만, 자바스크립트 런타임(Brower, Node)은 싱글 스레드가 아니라고 표현 할 수 있다.

아래의 코드가 어떻게 동작하는지 확인해보자.

console.log('Hi')
setTimeout(() => cb(){ 
   consol.log('ladin');
},5000)
console.log('my name is')
  1. 'Hi'가 출력된다!
  2. setTime cb가 stack에 쌓인다.
  3. setTime cb가 Web Api로 이동
  4. my name is 가 출력된다.
  5. timer 5초가 지나 cb가 callBack Queue 대기열에 들어간다.
  6. cb가 cabllback Queue에서 call Stack에 들어간다.
  7. cb가 실행되며 log ladin이 출력된다!🙂

여기까지 CallStack에서 Web Api 그리고 CallBack Queue가 동작하는 로직을 간단하게 살펴보았다.
즉, Web Api는 DOM, AJAX, setTimeout 등 브라우저가 제공하는 API를 가지고 있으며 JavaScript 엔진의 쓰레드와는 다른 별도의 쓰레드로 이루어져 있다.
CallBack Queue는 웹 API를 처리하고 있던 쓰레드로부터 전달받은 콜백 함수들의 실행을 기다리는(대기열) 곳이다. 여기에 저장된 콜백 함수들은 스택이 비는 순간 스택에 순서대로 푸시 된다.

2-2 🤔이벤트 루프(EventLoop)가 그래서 뭐야?

콜 스택에서 부터 웹 Api, 콜백 큐 그리고 다시 콜 스택까지 동작 하는동안 콜 스택이 비었을때 콜백 큐에서 콜 스택 으로 푸시 한다. 그렇다면 누가 매 순간 스택이 비어있는지 여부와 콜백 큐에 콜백 함수가 기다리고 있는지 여부를 계속 확인 하며 감시할까 그 친구가 바로 이벤트 루프이다.
이벤트 루프는 이름 그대로 같은 작업을 무한히 반복하는 무한 루프라고 생각하면 이해하기 쉬울 것이다. 참고로 이렇게 매 순간 태스크 큐와 스택을 확인하는 작업을 틱(Tick)이라고 부른다.

🥸마무리

이번 EventLoop에 대해 공부하며 Javascript가 어떻게 동작하는지 또 왜 non-bloking, 동시성을 가지는지 확실히 이해를 하게된 계기가 되었다.
설명 대부분이 웹브라우저 환경 기반으로 글을 작성하여 Node에서는 어떻게 동작되는지 찾아 보았는데 Nods.js는 비동기 지원을 위해 libuv라이브러리를 사용하며, 이 libuv 가 이벤트 루프를 제공한다. 자바스크립트 엔진은 비동기 작업을 위해 노드js의 api를 호출하며, 이 때 넘겨진 콜백은 libuv의 이벤트 루프를 통해 스케쥴되고 실행 된다고 한다.
CallStack의 Stack 하나마다 execution context(실행컨텍스트)가 만들어지는데 이어서 실행 컨텍스트를 딥 다이브 후 정리해 볼 예정이다.

참고 자료
영상
https://www.youtube.com/watch?v=8aGhZQkoFbQ&t=1422s

https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
https://medium.com/@Rahulx1/understanding-event-loop-call-stack-event-job-queue-in-javascript-63dcd2c71ecd
https://medium.com/@gaurav.pandvia/understanding-javascript-function-executions-tasks-event-loop-call-stack-more-part-1-5683dea1f5ec

profile
Front-end developer

0개의 댓글