티스토리 뷰
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() 이 실행될 때 콜백이 바로 실행되지 않고 비동기로 진행된다는 뜻이다.
여기서 선행되어야 하는 지식이 있는데 바로 자바스크립트 이벤트 루프다. 이벤트 루프를 이해하면 동기 코드와 비동기 코드가 어떤 과정을 거쳐 실행되는지 알 수 있다. 마이크로태스크도 이와 거의 동일하나 (매크로)태스크보다 우선순위가 높다.
- 호출 스택에 모든 실행 함수들이 추가되고 값을 반환하면 호출 스택을 빠져나온다.
- 호출 스택이 비어 있으면 마이크로태스크 큐에 있던 태스크들이 하나씩 호출 스택에 추가되고 실행된다.
- 호출 스택과 마이크로태스크 큐가 모두 비어있게 되면 이벤트 루프는 (매크로)태스크 큐를 확인하고 남아있는 작업을 수행한다.
해당 내용은 이 링크를 참고하였고 위 과정을 시각적으로 표현해주어 아주 쉽게 이해가 가능하다.
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 에러 핸들링 가능.
'프론트엔드' 카테고리의 다른 글
[번역] 프론트엔드 개발자가 알아야 할 숫자들 (Latency Numbers) (1) | 2024.06.06 |
---|---|
프론트엔드 E2E 테스트 "잘" 적용하기 (컨벤션) (0) | 2024.01.11 |
[JS] 프라미스 API 중 Promise.all 직접 구현해보기 (0) | 2020.11.04 |
[JS] 프라미스 Promise 알아보기 (2) | 2020.10.28 |
[JS] try...catch 그리고 에러 핸들링 (0) | 2020.10.18 |
- Total
- Today
- Yesterday
- 인프런
- 리액트훅
- EventLoop
- Component
- 우아한테크러닝
- typeScript
- 프론트엔드
- asyncawait
- vue-meta
- vue-cli
- jsconf
- prerender-spa-plugin
- til
- chartjs
- nodejs
- JavaScript
- axios
- 상태관리
- Vuex
- js
- vue
- vue-router
- promise
- REACT
- ReactNative
- ES6
- Python
- frontend
- Docker
- vuejs
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |