JavaScript

빙글빙글 이벤트 루프 🏃‍♀️➰

bas96 2021. 8. 14. 00:21

목차
- Process & Thread
- MultiTreading

- Javascript  엔진?
- Javascript 실행 환경 ( Javascipt와 Web APIs가 어떻게 함께 협업할까?)

- 이벤트 루프 예시로 개념 정리 
- 렉 걸리게 하기.
- Task Queue VS Microtask Queue


# Process와 Thread의 차이 

Process

Process란 '운영체제 위에서 연속적으로 실행되는 프로그램'입니다.

핸드폰에서 어플리케이션을 실행하다가 갑자기 하나의 앱이 강제로 종료된 적이 있으신가요? 

그것이 프로세스가 죽었다라고 말합니다.

 

각 프로세스는 메모리에서 실행되고 있는 프로그램입니다.

프로세스 안에는 프로그램을 위한 code, 함수에 대한 정보가 저장된 stack, 데이터가 저장되는 heap, data로 구성됩니다.

 

Thread

Thread는 한 프로세스 안에서 어러 개 배정이 가능한 것으로, 각각 쓰레드 안에는 stack이 할당됩니다.

 

✅ 정리:

process는 프로그래밍을 동작하는 최고의 단위이고, thread는 프로그램 안에서 동시에 여러 개가 수행될 수 있는 작은 일꾼 단위입니다.

# MultiTreading이란?

한 프로세스 안에서 여러 스레드가 동시다발적으로 일어나는 것을 MultiTreading이라고 합니다.

멀티스레드를 가지고있으면 동시적으로 여러가지 일을 수행할 수 있기에 프로그램이 효율적으로 동작할 수 있습니다.

 

Java 언어는 언어 자체에서 멀티스레드가 작동한다고 합니다.

반면에 제가 쓰는 Javascript는 Single Threaded 언어입니다. 즉, 자바스크립트는 멀티스레딩이 없다는 말이죠 ㅠㅠ 

 

그런데! 우리가 보는 웹페이지에서는 데이터를 받으면서 여러가지 일을 합니다.

그 이유는~ 자바스크립트가 동작하는 '브라우저'(브라우저도 프로그램입니다.)안에 여러가지 Tread가 들어있기 때문입니다.

 

그래서 브라우저에서 제공하는 Web APIs를 이용하면 MultiTreading이 가능해 지는 것입니다.

 


# Javascript 엔진

자바스크립트 엔진은 메모리 힙과 콜스택으로 나누어져 있습니다.

 

저번에 실행 컨텍스트를 주제로 포스팅 하였을 때, 스택과 큐 자료구조가 잠깐 나왔습니다.

다시 한번 더 정확하게 개념을 정리하자면 

스택과 큐는 자료구조의 종류로, 스택은 LIFO(Last In First Out) 큐는 FIFO (First in first out)을 말합니다.

 

메모리 힙 에서는 변수를 선언하고 오브젝트를 할당하거나, 문자열 숫자열 등의 데이터가 저장되는 곳입니다.

이곳에서는 구조적으로 정리되어있지는 않습니다.

 

콜스텍 에서는 함수를 실행하는 순서에 따라 차곡차곡 쌓입니다. 

프로세스와 스레드는 각각 콜스텍을 가지고있는데 콜스텍은 사이즈가 있습니다.

주의할 점은 콜스텍을 잘못쓰는 경우로는, 함수 안에서 자기 자신을 계속 부르는 재귀함수를 사용할 시 콜스텍이 초과될 수 있습니다.

# Javascript  + Web APIs (Javascript 실행 환경)

브라우저에서 Web APIs로 다양한 것을 할 수 있고, 브라우저의 멀티 쓰레딩을 이용해 동시에 다양한 것을 할 수 있다고 하였습니다.

 

브라우저와 자바스크립트가 어떻게 협업할까요? 여기에서 ➰이벤트루프➰라는 개념이 나옵니다.

 

Web APIs는 예를들면 setTimeout , fetch, eventListener, DOM APIs 등이 있습니다.

이런 Web APIs를 사용할 때 우리가 콜백을 등록해놓으면, 원하는 이벤트가 발생할 때 웹 APIs 는 task Queue에 우리가 등록한 콜백을 task Queue 안으로 넣습니다.

그리고 콜스텍에 일이 남아있으면 콜스텍이 비워질 때 까지 기다린 뒤, 비워지면 이벤트루프는 task Queue에 있는 콜백을 콜스텍으로 가져옵니다.

그럼 자바스크립트 엔진이 콜스텍안에 있는 콜백을 실행시킵니다.

 

✅ 정리:

이벤트루프는 프로세스가 동작하는 동안 계속 빙글빙글 돌면서 task Queue와 call stack을 관찰하고,

call stack이 비워지는 순간, task Queue에 있는 것들을 콜스택으로 가져와서 자바스크립트 엔진이 수행될 수 있게 한다.

 

이해하기

 


# 콜백 안의 순서는 상관이 없다.

버튼에 클릭 리스너를 등록했다고 가정해봅시다.

 

const button = document.querySelector('button');

button.addEventListener('click', () => {
    // 어떤 등록 1, 2, 3이 모두 완료되면 그걸 가지고 랜더링
    // 어떤 등록 1
    // 어떤 등록 2
    // 어떤 등록 3
});

 

이 코드블럭은 클릭이벤트가 발생하면 -> 나머지 콜백을 task Queue에 넣겠죠?

여기서 콜백 안에 작성된 코드는 어떤 순서를 하든지 상관이 없습니다.

왜냐하면 콜백이 콜백 스텍에 들어가는 순간, 이벤트루프는 이것이 다~~ 실행 될 때까지 기다렸다가!

나중에 랜더링이 될 때 전체적으로 적용된 아이들이 layout, paint에 걸쳐 브라우저에 표기되기 때문입니다.

 

즉, 우리가 웹 APIs의 하나인 addClickListener라는 함수를 이용해서 callback function을 등록해 놓았습니다.

클릭 이벤트가 발생하면 web apis는 콜백함수를 테스크큐에 등록한다.

이벤트리스너: 👶🏻

👶🏻: Task Queue에 callback이 있네? callstack로 가져와야지

그리고 콜스택이 완료되어 사라지면,

👶🏻: 이 수정된 사항들을 다 적용해서 랜더링 하자~

(랜더링이 발생할 때에는 최종적으로 업데이트 된 것 기준으로 랜더트리가 만들어 진다음에 레이아웃이 만들어지고 표기가 되는 것!)

# 이벤트 루프가 콜스택에 오랫동안 머물러있도록 하지말자.

이벤트루프가 콜스텍에 오랫동안 머물러있으면 브라우저가 업데이트 되지 않고, 클릭과 같은 사용자의 이벤트처리가 되지 않습니다.

이런 경우를 우리는 흔히 렉이 걸렸다 라고 말합니다.

즉 콜백은 간다하게 작성하는 것이 좋고, 재귀함수같은 아이들은 조심해서 사용하는 것이 좋습니다.

# Task Queue와 Microtask Queue차이점

간단하게 설명해보면, setTimeout은 task queue를, Promise callback은 microtask queue를 이용합니다.

 

Promise와 함께 쓰이는 .then/catch/finally 핸들러가 microtask queue가 됩니다.

이 또한 queue이기 때문에 FIFO를 따름니다.

이처럼 microtask는 다른 이벤트 핸들러나 렌더링 작업, 혹은 다른 매크로태스크가 실행되기 전에 처리됩니다.

 

아래 이미지가 아주 잘 설명해주는 것 같네요 :)