[TIL] 211013
📝 오늘 한 것
- await / error handling / await 병렬 처리 / Promise.all() / Promise.race()
📖 학습 자료
📚 배운 것
1. async · await
📚 배운 것
1. async · await
async는 [TIL] 211012 참고
2) await
promise를 기다리기 위해 사용된다
async가 붙은 함수 안에서만 사용이 가능하다
promise가 await에 넘겨지면
await은 Promise가 수행을 완료하기를 기다렸다가
해당 값을 리턴한다
function delay (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/* 1. async와 await 이용해 '동기적인 코드를 사용하는 것처럼' 작성 */
async function getApple () { // (3) 프로미스를 만든다
await delay(3000); // (1) delay(3000)가 끝날 때까지 기다렸다가
return '🍎'; // (2) 사과를 리턴하는
}
getApple()
.then(console.log); // 3초 뒤에 🍎 출력
/* 2. 원래라면 promise chaining을 이용해 '비동기식으로' 작성 */
function getBanana() {
return delay(3000)
.then(() => '🍌');
}
getBanana()
.then(console.log); // 3초 뒤에 🍌 출력
💡 사과와 바나나의 값을 모두 가져오는 함수를 만들어보자.
-
내가 작성한 답안
console 창에 출력되지 않음
getBanana 콜백 함수를 실행한 후 그 promise의 값을 return 하지 않았기 때문
// 오답
function pickFruits () {
return getApple()
.then(apple => getBanana(apple))
.then(banana => console.log(`${apple} + ${banana}`));
}
pickFruits();
-
강의 답안
- getBanana(apple)에서 인수 apple 안 써줘도 됨
- 인수 지운 getBanana() 앞에
return 추가
- console.log는 pickFruits 함수 호출 시 then() 안에서 사용되도록 위치 바뀜
→ 최종 결과에는 영향 주지 않지만, pickFruits 함수를 좀 더 깔끔하게 작성 가능(하지만 어쨌거나 콜백 지옥)
// 정답
function pickFruits () {
return getApple()
.then(apple => {
return getBanana()
.then(banana => `${apple} + ${banana}`);
});
}
pickFruits().then(console.log);
💡 async와 await을 이용해 콜백 지옥 탈출하기
promise가 await에 넘겨지면
await은 Promise가 수행을 완료하기를 기다렸다가
해당 값을 리턴한다
async function pickFruits () {
const apple = await getApple();
const banana = await getBanana();
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
이처럼 async와 await을 이용해 동기적으로 실행되는 코드를 작성하듯이 간편하게 작성할 수 있다.
💡 에러 처리
(Error Handling)
- pickFruits 함수 안에
try...catch 문
을 작성해 에러를 catch 할 수 있도록 한다. try...catch 문을 적지 않으면, console 창에 다음과 같이 '에러 발생!'을 잡을 수 없다는 찐 에러가 뜬다.(Uncaught (in promise) 에러 발생!)
async function getApple () {
await delay(3000);
throw '에러 발생!';
return '🍎';
}
async function pickFruits () {
try {
const apple = await getApple();
const banana = await getBanana();
return `${apple} + ${banana}`;
} catch(error) {
console.log(error); // 에러 발생!
}
}
pickFruits().then(console.log); // undefined
💡 await 병렬 처리
- 원래 코드
function delay (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// getApple 함수에서 사과를 받아오는 데 3초가 걸리고
async function getApple () {
await delay(3000);
return '🍎';
}
// getBanana() 함수에서 바나나를 받아오는 데도 3초가 걸린다
async function getBanana () {
await delay(3000);
return '🍌';
}
// 그런데
async function pickFruits () {
const apple = await getApple(); // (1) 사과를 받아올 때도 await을 써서 기다리도록 하고
const banana = await getBanana(); // (2) 바나나를 받아올 때도 await을 써서 기다리도록 하면
return `${apple} + ${banana}`; // (3)
}
// (1)이 끝나야만 (2)가 진행되고, (2)가 끝나야만 (3)이 진행되므로
// 원하는 결과를 얻기까지는 총 6초나 걸린다
// 사과와 바나나를 받아오는 코드는 완전히 독립적이므로, 이렇게 하는 것은 시간 면에서 비효율적이다
- 수정한 코드
function delay (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple () {
await delay(3000);
return '🍎';
}
async function getBanana () {
await delay(3000);
return '🍌';
}
async function pickFruits () {
// await이 없으므로 getApple(), getBanana()의 '병렬 실행이 가능하다.'
const applePromise = getApple(); // getApple 함수에 의해 생성된 promise는, applePromise 변수에 할당된다.
const bananaPromise = getBanana(); // getBanana 함수에 의해 생성된 promise는, bananaPromise 변수에 할당된다.
const apple = await applePromise;
const banana = await bananaPromise;
return `${apple} + ${banana}`;
}
🔥 코드 분석 (이해를 위해 apple 부분만 가져옴)
읽기 순서 (1) → (2) → (3)const applePromise = getApple(); // (2) 그런데 이미 (1) 이전에 promise가 생성된 순간 // promise 안의 코드 블럭은 바로 실행된 상태이므로 const apple = await applePromise; // (1) applepromise가 await에 넘겨지면 // await은 applePromise가 수행을 완료하기를 기다렸다가 해당 값을 리턴하고, // 이 값이 apple 변수에 할당된다. // (3) 여기서 applePromise 앞에 await을 써줬어도 추가로 기다릴 필요 없이 // 이미 진행 중인 applePromise의 수행이 완료되면 // applePromise의 resolve가 전달하는 🍎가 바로 apple 변수에 할당된다.
💡 그러나 위의 수정된 코드는 시간 면에서는 효율성이 개선되었을지 몰라도, 여전히 비효율적이다. 서로 독립적으로 수행 가능한 코드들은 promise.all()을 이용해 한층 간결하게 작성할 수 있다.
-
Promise.all()
매개변수로 promise 배열을 전달하면
모든 promise들이병렬적으로
다 받아질 때까지 모아준다
function pickAllFruits () {
return Promise.all([getApple(), getBanana()])
.then(fruits => fruits.join(' + '));
}
pikAllFruits().then(console.log); // 🍎 + 🍌
💡 어떤 과일이든 상관없이, 먼저 따지는 과일 하나만을 받아오고 싶은 경우
-
Promise.race()
배열의 요소들(promise들) 중
가장 먼저 값을 리턴하는 promise
를 반환한다.
function delay (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple () {
await delay(2000);
return '🍎';
}
async function getBanana () {
await delay(1000);
return '🍌';
}
function pickOnlyOne () {
return Promise.race([getApple(), getBanna()]);
}
pickOnlyOne().then(console.log); // 🍌
💡 과제 : async와 await을 이용해 기존 코드 다시 작성
① 콜백 지옥 → ② promise 이용해 콜백 지옥 탈출하기 → ③ async와 await을 이용해 코드 개선
- async와 await을 이용해 동기식으로 수정한 코드
class UserStorage {
loginUser (id, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id === 'syong' && password === 'happy' ||
id === 'corder' && password === 'nice') {
resolve(id);
} else {
reject(new Error('not found'));
}
}, 2000);
});
}
getRoles (user) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (user === 'syong') {
resolve({ name: 'syong', role: 'admin' });
} else {
reject(new Error('no access'));
}
}, 1000);
});
}
}
const userStorage = new UserStorage();
const id = prompt('id를 입력해주세요');
const password = prompt('password를 입력해주세요');
async function completeLogin () {
try {
const user = await userStorage.loginUser(id, password);
const nameAndRole = await userStorage.getRoles(user);
return `${nameAndRole.role} ${nameAndRole.name}님, 환영합니다!`
} catch(error) {
console.log(error);
}
}
completeLogin().then(alert);
✨ 내일 할 것
- 자료 구조와 알고리즘 책 읽기
Author And Source
이 문제에 관하여([TIL] 211013), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@leesyong/TIL-211013
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Author And Source
이 문제에 관하여([TIL] 211013), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@leesyong/TIL-211013저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)