티스토리 뷰

반응형

 

 

Promise 객체가 가진 핸들러들 .then(), .catch(), .finally() 는 모두 비동기적으로 실행된다.

console.log('Start!');
Promise.resolve('Promise!').then(res => console.log(res));
console.log('End!');

// 아래와 같이 출력
// Start!
// End!
// Promise!

위 코드를 보면 Promise를 바로 resolve 해주어도 then이 실행되는 것은 동기 코드보다 나중이다. 왜 그럴까?

 

마이크로태스크(Microtasks)와 매크로태스크(Macrotasks)

이 때 등장하는 것이 바로 마이크로태스크다.

ECMA에선 위와 같은 비동기 처리를 위해 PromiseJobs라는 내부 큐를 명시하는데 V8 엔진에서 '마이크로태스크 큐'(ES8 용어) 라고 부르기 때문에 이 용어가 좀 더 선호된다고 한다.

 

마이크로태스크에 앞서 매크로태스크가 존재하는데 둘 다 비동기 작업을 관리하기 위한 개념이지만 약간의 차이가 있다.

Microtasks Promise callback, process.nextTick(Node.js에서 사용하는 비동기 실행 함수), queueMicrotask
(Macro)tasks setTimeout, setInterval, setImmediate

위 표에서 보듯이 함수마다 다른 태스크가 적용되는데, Promise는 Microtasks에 속해있는것을 볼 수 있다.

Promise가 수행되고 .then(), .catch(), .finally() 등이 호출되면 각 함수에 넘겨진 콜백이 Microtask queue에 추가된다. 이 말인 즉슨, .then(), .catch(), .finally() 이 실행될 때 콜백이 바로 실행되지 않고 비동기로 진행된다는 뜻이다.

 

여기서 선행되어야 하는 지식이 있는데 바로 자바스크립트 이벤트 루프다. 이벤트 루프를 이해하면 동기 코드와 비동기 코드가 어떤 과정을 거쳐 실행되는지 알 수 있다. 마이크로태스크도 이와 거의 동일하나 (매크로)태스크보다 우선순위가 높다.

  1. 호출 스택에 모든 실행 함수들이 추가되고 값을 반환하면 호출 스택을 빠져나온다.
  2. 호출 스택이 비어 있으면 마이크로태스크 큐에 있던 태스크들이 하나씩 호출 스택에 추가되고 실행된다.
  3. 호출 스택과 마이크로태스크 큐가 모두 비어있게 되면 이벤트 루프는 (매크로)태스크 큐를 확인하고 남아있는 작업을 수행한다.

해당 내용은 이 링크를 참고하였고 위 과정을 시각적으로 표현해주어 아주 쉽게 이해가 가능하다.

 

async / await

async/await는 프로미스 코드를 동기적 코드처럼 다룰수 있게 해주는 편리한 문법이다.

async function f1() {
  return 1
}
f1() // Promise 객체 반환 (state: 'fulfilled', result: 1)
f1().then(console.log) // 1 출력

함수 선언시 function 앞에 'aynsc'를 넣어주면 Promise 객체를 반환하는 함수가 된다. 프로미스가 아닌 값을 반환하더라도 이행된 상태의 프로미스로 값을 감싸서 반환한다. 그래서 위 코드처럼 .then()을 호출해줄 수 있다.

 

await는 async가 선언된 함수 내부에서만 사용할 수 있다.

async function f2() {
  const res = await new Promise((resolve, reject) => {
    setTimeout(() => resolve('완료!'), 1000);
  });
  console.log(res);
}
f2() // 1초 뒤에 '완료!' 출력

자바스크립트 코드가 실행되다가 위와 같이 await 키워드를 만나면 해당 프로미스가 처리될 때 까지 기다렸다가 완료되면 아래 console.log를 실행한다.

 

실제로는 await 키워드를 만나면 async 함수의 실행이 일시정지되고 async 함수 전체가 마이크로태스크 큐로 삽입된다. 그래서 호출 스택이 비어있는 상태가 되고 이 때 엔진은 다른 스크립트를 실행하거나 이벤트 처리 등을 할 수 있기 때문에 CPU 리소스가 낭비되지 않는다.

특징 및 주의사항

  • 최상위 레벨 코드에서 await 사용 불가능.
  • 프로미스는 아니지만 프로미스와 호환 가능한 'thenable' 객체도 await와 함께 사용 가능.
  • 클래스 메소드에도 async 붙여서 사용 가능.
  • try...catch 문법으로 async/await 에러 핸들링 가능.

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함