JavaScript/이벤트

Trooper·2022년 8월 4일
0

JavaScript

목록 보기
12/13
post-thumbnail
post-custom-banner

자바스크립트에서 이벤트는 DOM에 접근하여 어떤 사건을 가능하게 해줍니다.

이벤트 루프와 동시성

자바스크립트의 특징 중 하나는 '단일 스레드'기반의 언어라는 점입니다.
단일 스레드라는 것은 동시에 하나의 작업만을 처리할 수 있습니다.
실제로 자바스크립트가 사용되는 환경을 보면 많은 작업이 동시에 처리되고 있습니다.
단일 스레드이지만 동시성을 가지는 것은 '이벤트 루프' 개념입니다.

브라우저 환경
브라우저 환경

구글의 V8을 비롯한 대부분의 자바스크립트 엔진은 크게 2개의 영역으로 나뉩니다.

Call Stack 호출 스택

작업이 요청되면 요청된 작업은 순차적으로 Call Stack에 쌓이게 되고 순차적으로 실행됩니다.
자바스크립트는 단 하나의 Call Stack을 사용하기 때문에 해당 task가 종료하기 전까지는 다른 어떤 task도 수행될 수 없습니다.

Heap 힙

동적으로 생성된 객체 인스턴스가 할당되는 영역입니다.

동시성을 제공하기 위해 필요한 비동기 요청처리는 자바스크립트 엔진을 구동하는 환경 브라우저가 당담합니다.

Task Queue 태스크 큐

비동기 처리 함수의 콜백 함수, 비동기식 이벤트 핸들러, Timer 함수의 콜백 함수가 보관되는 영역으로 이벤트 루프에 의해 특정 시점에 순차적으로 Call Stack으로 이동되어 실행됩니다.

Microtask Queue & Macrotask Queue

태스크 큐(Task Queue)는 마이크로 태스크 큐(Event Queue)매크로 태스크큐(Job Queue)로 나뉘어집니다.

API에 따라 마이크로 태스크 큐를 사용하거나, 매크로 태스크 큐를 사용합니다.

macrotasks: requestAnimationFrame, I/O, UI rendering, setTimeout, setInterval, setImmediate
microtasks: process.nextTick, Promises, queueMicrotask(f), MutationObserver

이벤트 루프를 통해 마이크로 태스크 큐가 실행된다면 마이크로 태스크들은 실행하면서 새로운 마이크로 태스크를 추가 할수 있습니다.
새롭게 추가된 마이크로 태스크도 큐가 빌 때까지 계속해서 실행됩니다.

하지만

이벤트 루프를 통해 매크로 큐가 실행된다면 매크로 태스크 큐만 실행 시킵니다.
매크로 태스크가 추가한 매크로 태스크는 다음 이벤트 루프가 실행 될 때까지 실행되지 않습니다.

Microtask Queue가 Macro Task Queue보다 먼저 실행됩니다.

Event Loop 이벤트 루프

Call Stack 내에서 현재 실행중인 task가 있는지 그리고 Event Queue에 task가 있는지 반복하여 확인합니다. 만약 Call Stack이 비어있다면 Event Queue 내의 task가 Call Stack으로 이동하고 실행됩니다.

이벤트 루프 동작 방식

Example

const func1 = () => {
  console.log('func1');
  func2();
};

const func2 = () => {
  console.log('func2');
  setTimeout(() => {
    console.log('Task Queue');
  }, 0);

  func3();
};

const func3 = () => {
  console.log('func3');
};

func1();

/*
func1
func2
func3
Task Queue
 */
  1. Call Stack 에 func1 -> func2 -> func3 순으로 실행됩니다. Task Queue에 Timer 함수의 콜백 함수가 보관됩니다.
  2. 이벤트 루프에서 Call Stack을 확인합니다.
  3. Call Stack이 비어있으면 Timer 함수를 Call Stack에 이동되어 실행됩니다.
const a = () => {
  return new Promise(resolve => {
    resolve('new micro task');
  }).then(res => console.log(res));
};
const b = () => {
  return new Promise(resolve => {
    resolve('new micro task2');
  }).then(res => console.log(res));
};
const promise = new Promise(resolve => {
  resolve('micro task');
}).then(res => console.log(res));

const func1 = () => {
  setTimeout(function () {
    console.log('macro task');
    setTimeout(() => {
      console.log('new macro task');
    }, 0);
  }, 0);
  console.log('fun1');
  func2();
};

const func2 = () => {
  console.log('func2');
  promise.then(a).then(b);

  func3();
};

const func3 = () => {
  console.log('func3');
};

func1();
/*
fun1
func2
func3
micro task
new micro task
new micro task2
macro task
new macro task
*/
  1. Call Stack에 fun1 -> func2 -> func3 순으로 실행됩니다.
  2. 이벤트 루프에서 Call Stack이 비어있는지 확인합니다.
  3. 이벤트 루프에서 Micro Task Queue를 확인합니다.
  4. Micro Task Queue를 실행합니다. Micro Task가 생성한 Micro Task도 실행 시킵니다.
  5. 이벤트 루프에서 Call Stack이 비어있는지 확인합니다.
  6. 이벤트 루프에서 Macro Task Queue를 확인합니다.
  7. Macro Task Queue를 실행합니다. Macro Task를 실행시킵니다.
    하지만 Macro Task가 생성한 Macro Task를 실행시키는 것이 아닙니다.
  8. 이벤트 루프에서 Call Stack이 비어있는지 확인합니다.
  9. 이벤트 루프에서 Macro Task Queue를 확인합니다.

Reference

profile
Web FrontEnd Developer
post-custom-banner

0개의 댓글