티스토리 뷰

  • 자바스크립트는 싱글 스레드이며, 이것은 Call Stack이 하나라는 이야기이다.
    • 자바스크립트는 기본적으로 작업들을 동기 작업을 한다는 의미이다.
  • 그런데 어떻게 자바스크립트에서 비동기 작업들이 가능한건지?
    • 이벤트 루프, Task queue가 있어서 가능하다

자바스크립트 엔진

 

Call Stack

  • 자바스크립트는 단 하나의 호출 스택(Call Stack)을 사용한다. 처리할 작업들은 차례대로 호출 스택(Call Stack)에 담아서 차례대로 처리한다.
  • 하나의 함수가 실행되고, 이 함수의 실행이 끝날 때까지 어떤 task도 수행될 수 없다 (동기 작업).

Task queue (대기실)

  • Task queue는 처리할 비동기 관련 작업들을 임시 저장하는 큐이다. 
  • 비동기 작업에 관련한 Web APIs(setTimeout, Ajax(API호출), 이벤트 등)에 작업은 Call Stack이 아닌 Task queue에 들어온다.
  • 이벤트 루프에 의해 Call Stack이 비워질 때마다 Task queue에서 처리해야할 작업들을 순차적으로 Call Stack으로 불러와서 실행하게 해주는 역할을 한다.
  • 자바스크립트 엔진에는 단일 스레드이기 때문에 Call Stack이 하나 존재하며, 자바스크립트 엔진 외부(브라우저 환경, Node.js 환경)에 Task queue가 존재한다.
  • 자바스크립트에서 비동기 작업이 가능한 이유 (이벤트 루프, Task queue)
  • 예시1 
    • 1. setTimeout함수가 실행되고, 그 안의 콜백 함수가 Task queue에 들어간다
    • 2. alert()가 Call Stack에서 실행되고 작업을 마친다.
    • 3. 이벤트 루프는 Call Stack이 비워졌는지, Task queue에 처리할 작업이 있는지 확인하고 실행한다
    • 4. Call Stack이 비워지고, setTimeout에서 지정한 시간이 지나면 Task queue에 있는 작업이 실행된다
    setTimeout(() => alert("World"), 1000);  // Hello
    alert("Hello");                          // World
  • 예시2
    • 1. 1이 call Stack에서 실행된다
    • 2. 0초라 할지라도 setTimeout함수이기 때문에 일단 Task queue에 들어간다
    • 3. 3이 call Stack에서 실행된다
    • 4. call Stack이 비워졌는지 보고, Task queue에 있던 작업이 실행된다
alert(1);                          // 1
setTimeout(() => alert(2), 0);     // 3
alert(3);                          // 2

 

Heap

  • 메모리 할당해준다
  • 변수, 함수, 객체(인스턴스) 등이 담겨져 있다

이벤트 루프(Event loop)

  • 이벤트 루프는 Call Stack이 비워질 때마다 Task queue에서 처리해야할 작업들을 실행하는 역할을 해준다.
    • Call Stack에서 현재 실행중인 작업이 없는지, Task queue에 작업들이 있는지를 반복적으로 확인하여 실행한다
    • Task queue에서 들어온 순서대로 처리한다

이벤트 루프 활용 예시

  • 엔진이 동기 작업을 할 때 동안엔 렌더링이 절대 일어나지 않는다. 이것은 작업을 처리하는 데 걸리는 시간이 짧으면 전혀 문제가 되지 않지만, 작업 처리에 긴 시간이 걸리면, 브라우저는 작업을 처리하는 동안에는 발생한 사용자 이벤트 등의 다른 새로운 작업들을 처리하지 못해서 ‘지연’ 또는 ‘멈춤’ 현상이 발생한다
  • (예시) 작업을 처리하는데 걸리는 시간이 길 때
    • alert창이 뜰 때까지, 다른 작업(마우스 오른쪽 버튼 클릭) 등을 할 수가 없다
    let i = 0;
    function count() {
      for (let j = 0; j < 1e9; j++) {
        i++;
      }
      alert("완료");
    }
    count();
  • (해결법) 작업 쪼개고, 비동기 처리 setTimeout을 이용하기
    • alert창이 뜨기 전에도, 다른 작업(마우스 오른쪽 버튼 클릭) 등을 할 수 있다
    let i = 0;
    function count() {
      do {
        i++;
      } while (i % 1e6 != 0);   // 작업 쪼개기
    
      if (i == 1e9) {
        alert("완료");
      } else {    
        setTimeout(count,0);    // 다시 함수 호출  
      }
    }
    
    count();
    • 1. Call Stack에 있는 부분 작업을 마치고,
    • 2. setTimeout함수를 실행하고, 콜백 함수를 Task queue에 넣는다
    • 3. 이벤트 루프는 Call Stack이 비워졌는지, Task queue에 처리할 작업이 있는지 확인하고 실행한다
    • 4. Call Stack이 비워지고, setTimeout에서 지정한 시간이 지나면 Task queue에 있는 작업이 실행된다
    • 5. 나머지 부분 작업을 마친다
    • 6. 이런 식으로 무거운 작업을 쪼개고, 중간 중간에 이벤트 루프가 돌아갈 수 있게 비동기 처리를 해서 Task queue의 작업이 들어가도록 ‘환기’를 해주면, 다른 동작(사용자 이벤트 등)에 반응하면서 무거운 테스크 처리가 가능해진다

Microtask queue

  • 사실 Call Stack, Task queue외에도 Microtask queue가 존재한다.
  • 비동기 관련 Web APIs(setTimeout, Ajax(API호출), 이벤트 등) 작업 중에서 모두 Task queue로 들어가는 것이 아닌 Promise의 then()일 경우 Microtask queue로 들어가게 된다.

실행되는 순서는 Call Stack Microtask queue Task queue 이다.

이벤트 루프Call StackCall Stack 비워져있는 것 확인 후Microtask queue확인 후 Call Stack으로 넣어줘서 실행Call Stack과 Microtask queue이 비워져있는 것 확인 후Task queue확인 후 Call Stack으로 넣어줘서 실행되게 해준다.

그래서 코드로 구현을 하면 다음과 같은 순서로 실행되는 것을 확인할 수 있다.

console.log("script start");

setTimeout(function() {
  console.log("setTimeout");
}, 0);

Promise.resolve().then(function() {
  console.log("promise1");
}).then(function() {
  console.log("promise2");
});

console.log("script end");

/* 순서
script start
script end
promise1
promise2
setTimeout
*/

 

 

 

 

댓글
Total
Today
Yesterday