일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- express
- 독서 리뷰
- node.js
- 습윤밴드
- dql
- wecode
- 드림코딩
- Til
- SQL
- node
- JavaScript
- 메디패치
- 크로스핏
- nodejs
- MySQL
- dml
- github
- git
- Udemy
- 운동일지
- 러닝
- 위코드
- 월별 운동일지
- 홈트
- 걷기
- 활동 킬로칼로리
- 박스점프
- 달리기
- axios
- code kata
- Today
- Total
RISK IT
[TIL18_23.1.26.] promise와 async, await (드림코딩 강의) 본문
1. Promise
promise란
- Javascript에서 제공하는 비동기를 콜백함수 대신에 간편하게 처리할 수 있도록 도와주는 오브젝트
- 정해진 장시간의 기능을 수행하고 나서 정상적으로 기능이 수행되었다면 성공의 메시지와 함께 처리된 결과값 전달, 기능이 제대로 수행되지 않았다면 에러를 전달.
promise의 포인트 두 가지
- State (상태)
- process가 heavy한 작업을 수행하고 있는 중인지 또는 기능 수행을 다 완료해서 성공했는지, 실패했는지에 대한 상태에 대해서 이해해야 함
state
: pending (작업 진행 중) -> fulfilled (작업 완료) or rejected (작업 실패)- resolve도 reject도 입력되지 않으면 pending 상태에 있게 됨.
- producer와 consumer의 차이
- 정보를 제공하는 producer와 정보를 사용하는 consumer의 다른 두 가지 견해를 이해해야 함
1. Producer
보통 프로미스 안에서 heavy한 일을 함. 네트워크에서 데이터를 받아오거나 또는 파일에서 큰 데이터를 읽어오는 작업은 시간이 걸리기에 동기적으로 실행하게 되면 다음 작업을 진행할 수 없음. 따라서 이러한 작업은 비동기적으로 실행하는 것이 좋다.
프로미스 안에 네트워크 통신 등의 작업을 하는 코드를 작성할 때 프로미스가 만들어지는 그 순간 네트워크 통신 실행
만약 네트워크 통신을 사용자가 요청하는 순간에만 진행해야 한다면, 프로미스를 만드는 순간 콜백함수가 실행되기 때문에 이 점을 유의해야 한다.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ellie');
}, 2000);
});
프로미스 안에서는 작업을 성공적으로 수행했다면 ,resolve()
라는 콜백함수를 통해 데이터를 받아오면 된다.
2. Consumer
then, catch, finally를 통해 값을 받아올 수 있다.
예시
promise.then((value) => {});
// value에는 프로미스의 resolve 값이 들어간다.
.then()
: 프로미스가 정상적으로 잘 수행되어서 최종적으로 resolve를 통해 전달한 값을 value라는 parameter에 저장시킨다.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('no network'));
}, 2000);
});
Error()
는 자바스크립트에서 제공하는 오브젝트 중 하나. 에러가 발생했다는 것을 알려주는 오브젝트Error()
에는 에러가 발생한 정확한 이유를 명시해 줘야 한다.
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
});
에러는 .catch()
라는 함수를 이용하여 에러가 발생했을 때 어떻게 처리할 것인지 콜백 함수를 등록해주면 된다.
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
console.log('finally');
});
.finally()
는 성공하든 실패하든 상관 없이 무조건 실행시켜주는 함수
3. Promise Chaining
✍️ 입력
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
});
fetchNumber
.then((num) => num * 2)
.then((num) => num * 3)
.then((num) => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(num - 1), 1000);
});
})
.then((num) => console.log(num));
💻 출력
5
4. Error Handling
✍️ 입력
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = (hen) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${hen} => 🥚`), 1000);
});
const cook = (egg) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen()
.then((hen) => getEgg(hen))
.then((egg) => cook(egg))
.then((meal) => console.log(meal));
💻 출력
🐓 => 🥚 => 🍳
✍️ 입력
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = (hen) =>
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000);
});
const cook = (egg) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen()
.then((hen) => getEgg(hen))
.then((egg) => cook(egg))
.then((meal) => console.log(meal));
💻 출력
Error: error! 🐓 => 🥚
at promise.js:42:29
에러를 처리하는 .catch()
를 사용하지 않아 에러 발생
참고
콜백함수를 전달할 때 받아오는 값으로 다른 함수를 바로 호출하는 경우에는 받아오는 값 생략 가능.
getHen() //
.then(getEgg)
.then(cook)
.then(console.log);
✍️ 입력
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = (hen) =>
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000);
});
const cook = (egg) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen() //
.then(getEgg)
.catch((error) => {
return '🥯';
})
.then(cook)
.then(console.log)
.catch(console.log);
💻 출력
🥯 => 🍳
에러를 핸들링하기 위해서 .catch()
를 이용하여 에러가 발생했을 때도 promise chain이 실패하지 않고 실행됨
저번에 작성했던 callback 지옥 함수를 promise를 사용하여 간단하게 작성
class UserStorage {
loginUser(id, password, onSuccess, onError) {
setTimeout(() => {
if (
(id === 'ellie' && password === 'dream') ||
(id === 'coder' && password === 'academy')
) {
onSuccess(id);
} else {
onError(new Error('not found'));
}
}, 2000);
}
getRoles(user, onSuccess, onError) {
setTimeout(() => {
if (user === 'ellie') {
onSuccess({ name: 'ellie', role: 'admin' });
} else {
onError(new Error('no access'));
}
}, 1000);
}
}
const userStorage = new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your password');
userStorage.loginUser(
id,
password,
(user) => {
userStorage.getRoles(
user,
(userWithRole) => {
alert(
`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`
);
},
(error) => {
console.log(error);
}
);
},
(error) => {
console.log(error);
}
);
class UserStorage {
loginUser(id, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (
(id === 'ellie' && password === 'dream') ||
(id === 'coder' && password === 'academy')
) {
resolve(id);
} else {
reject(new Error('not found'));
}
}, 2000);
});
}
getRoles(user) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (user === 'ellie') {
resolve({ name: 'ellie', role: 'admin' });
} else {
reject(new Error('no access'));
}
}, 1000);
});
}
}
const userStorage = new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your password');
userStorage
.loginUser(id, password)
.then(userStorage.getRoles)
.then((user) => alert(`Hello ${user.name}, you have a ${user.role} role`))
.catch(console.log);
2. async, await
async, await이란?
async
,await
은promise
가 조금 더 간결하고 동기적인 것처럼 보이도록 만들어준다.- promise chaining을 통해
.then()``.then()
을 계속해서 추가할 수 있지만 코드가 조금 난잡해질 수 있다.async
,await
은 이를 조금 더 간편하게 동기식으로 작성할 수 있도록 도와준다. - 완전히 새로운 것이 아니라, 기존에 존재하는 promise 위에서 혹은 기존에 존재하는 promise를 감싸서 조금 더 간편하게 사용할 수 있도록 하는
syntactic sugar
의 역할을 한다. (또 다른 syntactic sugar로는 class가 있음) - 무조건 async, await없는 promise가 나쁜 것은 아님!
기존 Promise 적용 코드 예시
✍️ 입력
function fetchUser() {
return new Promise((resolve, reject) => {
resolve('ellie');
});
}
const user = fetchUser();
console.log(user);
💻 출력
Promise {<fulfilled>: 'ellie'}
같은 코드를 async 활용한 예시
✍️ 입력
function fetchUser() {
return new Promise((resolve, reject) => {
resolve('ellie');
});
}
const user = fetchUser();
console.log(user);
💻 출력
Promise {<fulfilled>: 'ellie'}
ellie
✍️ 입력
async function fetchUser() {
return 'ellie';
}
const user = fetchUser();
user.then(console.log);
console.log(user);
💻 출력
Promise {<fulfilled>: 'ellie'}
ellie
await promise 예시
✍️ 입력
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getApple() {
await delay(1000);
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
function pickFruits() {
return getApple().then((apple) => {
return getBanana().then((banana) => `${apple} + ${banana}`);
});
}
pickFruits().then(console.log);
💻 출력
🍎 + 🍌
프로미스도 너무 중첩해서 사용하게 되면 콜백 지옥과 비슷해짐
await과 async를 활용하여 더 간단하게 표현
✍️ 입력
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getApple() {
await delay(1000);
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
async function pickFruits() {
const apple = await getApple();
const banana = await getBanana();
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
💻 출력
🍎 + 🍌
위 코드는 조금 더 간단해졌지만, pickFruist()
에서 getApple()
를 호출할 때 1초가 걸리고 그 후 getBanana()
를 호출할 때 또 1초가 걸린다. apple과 banana는 독립적으로 실행할 수 있기 때문에 이런 방식은 비효율적이다.
이 때 프로미스를 만들면 프로미스가 바로 실행된다는 점을 이용해서 다음 코드와 같이 작성하면 더 효율적으로 코드를 실행할 수 있다.
✍️ 입력
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getApple() {
await delay(1000);
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
async function pickFruits() {
const applePromise = getApple();
const bananaPromise = getBanana();
const apple = await applePromise;
const banana = await bananaPromise;
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
💻 출력
🍎 + 🍌
하지만 병렬적으로 코드를 수행하는 경우에는 위에 있는 코드보다 더 깔끔하게 작성할 수 있다.
✍️ 입력
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getApple() {
await delay(1000);
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
function pickAllFruits() {
return Promise.all([getApple(), getBanana()]).then((fruits) =>
fruits.join(' + ')
);
}
pickAllFruits().then(console.log);
💻 출력
🍎 + 🍌
Promise.all()
이라는 API는 프로미스 배열을 전달하게 되면 모든 프로미스들이 병렬적으로 받아질 때까지 모아주는 역할을 한다..then()
이후에 모아진 프로미스들을 .join(' + ')
로 묶어줬다.
만약 먼저 따지는(출력되는) 과일만 출력하고 싶다면 다음과 같은 코드를 작성하면 된다.
✍️ 입력
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(), getBanana()]);
}
pickOnlyOne().then(console.log);
💻 출력
🍌
.race()
API는 배열에 전달되는 프로미스 중 가장 먼저 값을 리턴하는 프로미스만 전달시킨다.
'IT > TIL' 카테고리의 다른 글
[TIL20_23.1.28.] [NodeJS] Node 서버 생성 (0) | 2023.01.28 |
---|---|
[TIL19_23.1.27.] [Node] Express - 'westagram' feature/CRUD 코드 수정 (0) | 2023.01.27 |
[TIL17_23.1.25.] callback 함수 with 동기&비동기 (드림코딩 강의) (0) | 2023.01.25 |
[TIL16_23.1.24.] git 공부 & code kata 복습 (0) | 2023.01.24 |
[TIL15_23.1.23.] [Node] Express - 'westagram' 특정 유저 게시글 조회 SQL문 수정 (0) | 2023.01.24 |