-
Javascript Event Loop 자바스크립트 이벤트 루프Front-end/Javascript 2021. 7. 8. 11:23반응형
비전공자로써 처음 Javascript를 공부하기 시작한 분들이 아마 다 비슷비슷할 것이다.
처음부터 var, let, const 등을 시작으로 문자열, 객체, 클래스, 함수 등을 배우게 된다.
즉, 눈 앞에 코드를 쳐보면서 시작을 하는 것이다.
처음에는 그런 것이 재미있게 느껴졌기 때문에 시작을 하는 입장에서는 좋은 방법이라고 생각은 든다.
하지만 이제는! Javascript의 맛을 조금 봤다면, 발을 조금 담궈봤다고 한다면!
Javascript의 Event Loop에 대해서도 공부를 해봐야 할 것이다.
" Event Loop를 모른다면 Javascript를 안다고 말할 수 없다 " 라는 말을 하는 만큼 Javascript를 사용하는 분들이라면 누구든지 이벤트 루프에 대해서 알아야 할 것이다!
우선은 전체적인 모양을 머리 속에 넣어보도록 하자!
물론 이벤트루프라는 것이 살아있는, 어떤 형체가 있는 것은 아니지만 나의 경우 Redux도 그러했지만, 이런 식으로 머리 속에 이미지를 그리고 나면 이해하기가 수월하다고 느껴졌다.
우선 Javascript는 Single thread로 작동한다. 하지만 정확하게 말하면 Multi thread이다.
이게 무슨 말일까?
Javascript는 C language로 만들어졌따.
C의 경우 멀티스레드인데, 쉽게 말하자면 그 중 하나가 Javascript를 담당하고 있으므로 싱글 스레드라고 하는 것이다.
하지만 비동기처리 등의 경우 멀티스레드로 작동하기 때문에 이런 개념만 알고 있으면 될 것 같다.
구글의 V8 JS엔진을 비롯하여 대부분의 Javascript는 크게 2개의 영역으로 구분 할 수 있다.
바로 메모리 힙(Memory Heap), 콜 스택(Call stack) 이다.
메모리 힙은 메모리 할당이 일어나는 곳인데, 힙이라는 것은 구조화 되지 않은, 정리되지 않은 넓은 메모리 영역을 말한다.
이곳에는 reference value가 들어가게 된다.
콜 스택은 호출스택이라고도 불리는데 Javascript는 인터프리터 언어(Interpreter Language)이므로 한줄 씩 실행이 되는데, 이 때 실행 될 코드의 한 줄 단위로 콜 스택이 할당 된다.
동기적으로 작동할 때는 stack이라는 단어처럼 벽돌 쌓듯이 위로 쌓아지고, 위에서부터 실행이 되는 형태를 보이며, primitive value가 들어간다.
/* 잠깐! primitive와 reference의 메모리 저장에 대해🎃 */
메모리에는 크게 힙과 콜스택이 있다.
힙에는 동적으로 생성되어지는 배열, 객체, 함수와 같은 primitive value가 저장되고, 스택에는 변수가 저장된다.
( primitive value 라는 것은 우리말로 원시 값이라는 것인데, 그 종류로는 string, number, bigint, boolean, undefined, symbol, null 이렇게 7가지가 있다. )
이러한 primitive value를 사용 한다면 스택에 위치하고 있는 primitive value를 복사해와서 값을 그대로 할당하게 된다.
reference value의 경우라면 변수는 스택에 있는 채로 객체나 배열, 함수만 힙 메모리에 생성해서 변수가 힙메모리 생성 된 그 공간을 가리키게 한다!동기적인 코드의 예시를 살펴보자!
function first() { second(); console.log('First') } function second() { third(); console.log('Second') } function third() { console.log('third') } first(); third();
이런 코드는 어떤 순서로 출력이 될까?
답은 아래와 같다.
third
Second
First
third이렇게 나타나는 이유에 대해서 콜 스택을 알고 나면 도움이 된다.
콜스택에서는 어떤 일이 벌어지고 있을까?
== The Call Stack Section == console.log('Third') third() second() first() anonymous =============================
콜스택에서는 가장 아래부터 anonymous가 나타나고 그 위로 순서대로 쌓이게 된다.
그리고 앞서 말한 것 처럼 위에서부터 아래로 실행이 된다.
함수가 먼저 실행 되는 것이 아니라 함수는 잠시 따로 빠져있다가 함수 실행 코드가 먼저 실행된다.
즉, 함수 실행 코드 중 가장 위에 있는 first()가 실행되고, first 함수에 들어있는 second() 함수가 쌓인다.
그리고 second() 함수가 실행 되면서 third() 함수가 나타나고 그 안에 있는 console.log('third')가 스택에 쌓이게 된다.
쌓인 순서에서 반대 순서로 실행이 되니 위와 같은 형태로 실행된다.
이미지 상에서 그 옆에 있는 Web APIs는 무엇일까?
이는 쉽게 말해 setTimeout, setInterval과 같은 Timer function, DOM API, HTTP request, 이벤트 핸들러 등과 같은, 비동기 처리를 담당하는 곳이라고 할 수 있다.
브라우저 상에서는 Web APIs라고하고, Node에서는 백그라운드(Background)라고 말한다.
이미지 상에서 그 아래에 있는 콜백 큐(Callback Queue)는 무엇일까?
이는 Task Queue(or Event Queue)와 Microtask Queue(or Job Queue) 그리고 Animation Frames로 이루어져있다.
여러가지 이름을 알아둬야하는 이유는 누군가 설명을 할 때 이게 무엇인지 알아먹기 위해서이다...이들은 우선 순위가 나뉘는데 Microtask Queue > Animation Frames > Task Queue 순서로 진행이 된다.
비동기 처리한 것을 가지고 있다가 Event Loop가 계속 돌면서 call stack을 확인하게 되고, stack이 비었을 때 콜스택으로 보내어 실행시키게 된다.
이것이 이벤트루프의 역할인 것이다.
앞서 동기적인 코드의 진행을 봤다.
동기적인 경우에는 Web APIs나 콜스택을 볼 필요도 없이 Heap, Stack만 보고도 진행을 볼 수 있다.
그렇다면 비동기 코드의 이동은 어떤 식으로 진행이 될까?
콜 스택 -> Web APIs -> 콜백 큐 -> 콜 스택 -> 콘솔
이런 식으로 이동하게 된다.
Timer Function인 setTimeout으로 예를 들어보자!
console.log('start') setTimeout(function() { console.log('3초 후 실행'); }, 3000) console.log('end')
답은 너무 간단하다 "시작, 끝, 중간" 순서로 나타날 것이다.
하지만 문제는 어떻게 작동하는지 알아보는것이다.
우선 call stack에는 anonymous가 묻지도 따지지도 말고 먼저 들어가게 된다.
Javascript가 실행 됨녀 Global Execution Context라고 하는 것이 anonymouse라는 이름으로 call stack에 추가 된다.
첫번째로 console.log('start')가 call stack에 쌓이고 바로 console에 찍히게 된다.
두번째로 비동기 함수인 setTimeout(익명함수 '중간', 3초)이 call stack에 들어갔다가 실행되면서 browswer의 경우 Web APIs, Node의 경우 background로 넘어가게 된다.
Web APIs에서 3초를 돌리고 있는 사이에 console.log('end')가 call stack에 들어갔다가 console에 바로 찍히게 되며, anonymous가 실행되면서 call stack이 비어진다.
setTimeout의 콜백함수는 Web API's에서 빠져나와 콜백큐로 넘어가게 되는데, 이 때 이벤트루프가 계속해서 체크를 하면서 call stack이 비어있는지 확인하게 된다.
그리고 call stack이 비어있는 것을 확인하고 콜백큐의 익명함수를 call stack에 보내주게 된다. ( 이것이 이벤트 루프의 역할!)
이 때 익명 함수 안을 보면 3초후 실행 이라는 console.log가 있으니 실행이 되고 console에 찍히면서 익명함수가 종료되고, 해당 코드의 프로그램 동작이 마무리 된다.
반응형'Front-end > Javascript' 카테고리의 다른 글
uuid 설치 없이 unique ID 만들기 (crypto.randomUUID) (0) 2023.02.05 [json-server] 프론트엔드 연습용 백엔드 데이터가 필요하다면! (0) 2021.10.04 ES6에 추가 된 Map 객체 (0) 2021.09.26 자바스크립트 원시 값(Primitives values) : index[ ]로 string을 바꿀 수 없다! (0) 2021.07.21 Javascript Scope (스코프) 기초 (0) 2021.06.28