[딥다이브] 013. 제너레이터와 async/await
0. 개요
es8에서는 제너레이터보다 간단하고 가독성 좋게 비동기처리를 동기 처리처럼 동작하도록 구현할 수 있는 async/await이 도입되었다.
1. async
async 함수는 async 키워드를 사용해 정의하며 언제나 프로미스를 반환한다.
async & await : 깔끔하게 promise를 사용하는 방법
무조건 async & await >> promise가 아니라 경우에 따라 더 좋은 게 있다.
- promise
function fetchUser() {
return new Promise((resolve, reject) => {
resolve("ellie");
});
}
const user = fetchUser();
user.then(console.log);
- async 사용
async function fetchUser() {
return "ellie";
}
const user = fetchUser();
user.then(console.log);
2. await
await 키워드는 settled 상태(비동기 처리가 수행된 상태)가 될 때까지 대기하다가 settled 상태가 되면 프로미스가 resolve한 처리 결과를 반환한다.
- 정해진 ms가 지나면 resolve를 호출하는 promise를 리턴하는 함수
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
- delay가 끝날 때까지 기다렸다가 사과를 리턴
async function getApple() {
await delay(1000);
return "🍎";
}
- 동기적인 코드를 쓰는 것처럼 깔끔함
async function getBanana() {
await delay(1000);
return "🍌";
}
- promise로 썼다면 체이닝이 이어지는 느낌
function getBanana() {
return delay(1000).then(() => "🍌");
}
예를 들어 사과를 받고, 사과가 받아오지면 바나나를 받고, 바나나가 받아와지면 사과랑 바나나를 묶어서 리턴하는 상황을 설정해보자. 🍎 + 🍌
- 프로미스 체이닝 ... 콜백지옥 연상
function pickFruits() {
return getApple().then((apple) => {
return getBanana().then((banana) => `${apple}+${banana}`);
});
}
- async 사용
async function pickFruits() {
const apple = await getApple(); // 1초 소요
const banana = await getBanana(); // 1초 소요
return `${apple} + ${banana}`; // 2초 소요
}
pickFruits().then(console.log); // 🍎 + 🍌
3 에러처리
async/await에서 에러 처리는 try..catch문을 사용할 수 있다.
프로미스를 반환하는 비동기 함수는 명시적으로 호출할 수 있기 때문에 호출자가 명확하다.
async function getBanana() {
await delay(1000);
throw "에러 발생";
return "🍌";
}
- 기존의 에러 핸들링처럼 try..catch..를 사용함
async function pickFruits() {
try {
const apple = await getApple();
const banana = await getBanana();
} catch (err) {
console.log(err);
}
return `${apple} + ${banana}`;
}
4. await 병렬 처리
서로 연관없이 개별적으로 수행되는 비동기 처리이기 때문에, 앞선 비동기 처리가 완료될 때까지 대기해서 순차적으로 처리할 필요가 없다.
async function pickFruits() {
const apple = await getApple(); // 1초 소요
const banana = await getBanana(); // 1초 소요
return `${apple} + ${banana}`; //총 2초 소요
}
pickFruits().then(console.log); // 🍎 + 🍌
- 관련 예제(46-20)
async function bar(n) {
const 과일 = await new Promise((resolve) =>
setTimeout(() => resolve(n), 3000)
);
const 과일잼 = await new Promise((resolve) =>
setTimeout(() => resolve(과일 + 잼), <3000)
);
const 과일잼토스트 = await new Promise((resolve) =>
setTimeout(() => resolve(과일잼 + 토스트), 3000)
);
return 과일잼토스트;
}
bar(사과);
사과를 따서 사과잼을 만들어서 사과잼 토스트 먹을 게 아니라면(서로 연관 없이 개별적으로 수행해도 되면) 병렬 처리한다.
5. 유용한 promise api 사용
- Promise.all
promise의 배열을 전달하면, 모든 promise들이 병렬적으로 실행한다.
function pickAllFruits() {
return Promise.all([getApple(), getBanana()]).then((fruitsArray) =>
fruitsArray.join("+")
);
}
pickFruits().then(console.log); // "🍎 + 🍌"
- Promise.race
어떤 것이든 상관없고 먼저 따온 첫 번째 과일을 받아오는 상황을 상상해보자. 예를 들어 사과는 2초 걸리고 바나나는 1초 걸리는 경우라고 친다면 다음처럼 코드를 작성할 수 있다.
function pickOnlyOne() {
return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log); //🍌 1초만에 출력
promise의 배열을 전달하면, 모든 promise들이 병렬적으로 실행한다.
function pickAllFruits() {
return Promise.all([getApple(), getBanana()]).then((fruitsArray) =>
fruitsArray.join("+")
);
}
pickFruits().then(console.log); // "🍎 + 🍌"
어떤 것이든 상관없고 먼저 따온 첫 번째 과일을 받아오는 상황을 상상해보자. 예를 들어 사과는 2초 걸리고 바나나는 1초 걸리는 경우라고 친다면 다음처럼 코드를 작성할 수 있다.
function pickOnlyOne() {
return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log); //🍌 1초만에 출력
요약하자면 async, await은 promise를 간편하게 쓸 수 있고, promise는 all이나 race처럼 유용한 api가 있다.
Author And Source
이 문제에 관하여([딥다이브] 013. 제너레이터와 async/await), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@ongddree/딥다이브-013저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)