이벤트 루프, 태스크 큐, 비동기

otter·2022년 3월 1일
0

  • Web browser(& Node) is working in the background while the synchronous JS code is running.

single thread

  • 자바스크립트는 싱글 쓰레드 언어고, 단 하나의 콜스택만을 가지고 있다.
  • 콜스택은, 함수의 실행순서를 나타내는데 함수가 실행되면 push되고 return되면 pop된다.
  • 따라서, 자바스크립트에서는 콜스택 맨위에 있는 함수가 실행되는 도중 다른 함수가 실행되지 않는다.

blocking VS non-blocking

  • 만약, 현재 실행중인 함수가 네트워크 요청이나 또는, 큰 계산등 시간이 오래걸리는 작업이라면 어떻게 될까?
  • 자바스크립트는, 싱글쓰레드이고 하나의 콜스택을 가지고 있다는 점을 다시 생각해보면 자바스크립트는 현재 실행중인 함수의 요청이 끝날때까지 기다리다가 실행이 종료되면 다음으로 넘어간다.
  • 따라서 현재 실행중인 함수가 오래걸린다면 자바스크립트는 그냥 기다릴 것이다. 다른 task를 하지 않고.
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
  let myDate;
  for(let i = 0; i < 10000000; i++) {
    let date = new Date();
    myDate = date
  }

  console.log(myDate);

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});
  • MDN에 작성되어져 있는 극단적인 예시.
  • myDate와 관련한 계산이 천만번동안 실행되고 계산이 진행중일때 잠깐 브라우저가 멈춰 있는 것처럼 보이게 된다.
  • 계산이 다끝나고, console.log(myDate)까지 끝난 다음
  • p텍스트가 출력된다.
  • 콜스택에 어떠한 것이 남아 있으면 동기적으로 실행되는 콜스택상단의 진행중인 함수가 다른 함수의 진행을 막게 된다.

웹 API, 이벤트루프의 처리

  • 자바스크립트가 실행되는 런타임 환경에 존재하는 별도의 API이며, 브라우저가 지원한다.
  • DOM,setTimeout, fetch등이 있다.
  • 브라우저에서 실행되는 별도의 API이기 때문에, 자바스크립트의 콜스택에서 바로 사라진다.
    - 블로킹이 일어나지 않게 된다.
    - 비동기적 처리가 가능하게 된다.
  • 이벤트 루프는 이러한 과정에서 자바스크립트의 콜스택이 비어져 있는지, 태스크 큐에 대기중인 함수가 있는지를 계속해서 확인한다.
    - 자바스크립트의 콜스택이 비어져 있고, 태스크 큐에 대기중인 함수가 있다면 이벤트 루프는 태스크 큐에 대기중인 함수를 자바스크립트의 콜스택으로 옮겨 함수가 실행되도록 한다.

웹 API 예제

console.log('1');
setTimeout(() => {console.log('2')}, 1000);
console.log('3');
  • JS엔진은 console.log('1')을 처리하고,
  • setTimeout을 만나면, 이건 JS 엔진이 할 일이 아니니까 web API로 넘긴다.
  • web API에서 setTimeout 함수의 역할인 1000, 1초를 센다.
    - 이때, 자바스크립트의 콜스택은 비어져 있다. setTimeout은 web API가 처리하고 있는 중이다.
  • 콜스택이 비어져 있기 때문에 자바스크립트는 console.log('3')를 바로 처리한다.
  • 1초가 지난 후 Web API는 setTimeout의 콜백함수를 Callback queue로 보낸다.
  • 이벤트루프는, 자바스크립트의 콜스택이 비어져 있는지를 계속해서 확인한다.
    - 이때, 3번째 라인인 console.log('3');를 위에서 바로 처리했으므로, 콜스택은 비어져있다.
  • 이벤트루프는, 자바스크립트의 콜스택에 Callback queue의 가장 상단에 있는 -선입 선출이니까- console.log('2');push한다.
  • JS 엔진은 Call Stack에 푸쉬된 console.log('2')를 처리한다.
  • 여기서 Callback queuetask queue를 말함. (웹 API이기 때문)
console.log('1');
setTimeout(() => {console.log('2')}, 0);
console.log('3');
// -------- 시간이 0이더라도 똑같음. 위와같은 단계를 거치기 때문.

태스크 큐와 마이크로 태스크 큐

  • 웹 API가 처리하는 비동기 로직은, 자바스크립트 스펙에 존재하는 promise와 동일하다.
  • 그렇다면, 웹 API와 promise의 순서는 어떻게 진행될까?
  • 이를 알기 위해선 태스크 큐와 마이크로 태스크 큐를 구분해서 알아야 한다.
  • 태스크 큐는
    - setTimeout, DOM과 같이 웹 API가 처리하는 콜백함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역이다.
  • 마이크로 태스크 큐는
    - 자바스크립트 스펙에 있는 promise의 콜백이 일시적으로 보관되는 영역이다.
  • 여기서 중요한 점은, 마이크로 태스크 큐의 우선순위가 태스크 큐보다 높다는 것이다.

프로미스 비동기 예제

setTimeout(function() { // (A)
    console.log('A');
}, 0);
Promise.resolve().then(function() { // (B)
    console.log('B');
}).then(function() { // (C)
    console.log('C');
});
  • setTimeout 부분은 태스크 큐가,
  • Promise 부분은 마이크로 태스크 큐가 작동한다.
  • 마이크로 태스크 큐가 우선해서 콜스택에 들어가므로, (이벤트루프에 따라) Promise의 콜백이 먼저 실행된다.
  • 결과적으로 B -> C -> A 순으로 출력된다.

참고한 자료

https://www.youtube.com/watch?v=8aGhZQkoFbQ
https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Concepts
https://www.udemy.com/course/advanced-javascript-concepts/
https://meetup.toast.com/posts/89

profile
http://otter-log.world 로 이사했어요!

0개의 댓글