[JavaScript] 10. Promise
Promise란?
자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트
비동기를 처리할 때 콜백함수 대신 Promise 사용 가능
🔔 State (상태)
pending
: 프로미스가 만들어져서 우리가 지정한 오퍼레이션을 수행중인 상태
fulfilled
: 오페레이션을 성공적으로 끝낸 상태
rejected
: 파일을 찾을 수 없거나 네트워크에 문제가 생긴 상태
🔔 Producer vs Consumer
producer
: 우리가 원하는 기능을 수행해서 해당하는 데이터를 만들어냄
consumer
: 제공된 데이터를 소비함
Producer
우리가 원하는 기능을 비동기적으로 실행하는 producer 프로미스 만들기
const promise = new Promise((resolve, reject) => {
// doing some heavy work (네트워크에서 데이터를 받아오거나 파일에서 데이터를 읽어옴..)
console.log('doing something...');
// 프로미스 안에서 네트워크 통신을 하는 것처럼 시간의 delay를 줌
setTimeout(() => {
resolve('ann'); //성공적으로 네트워크 혹은 파일에서 데이터를 받어왔다면 resolve()라는 콜백함수를 통해 데이터를 전달
// reject(new Error('no network'));
}, 2000);
});
🌟 프로미스를 만드는 순간, 우리가 전달한 executor라는 콜백함수가 바로 실행됨
만약, 프로미스 안에 네트워크 통신을 하는 코드를 작성했다면, 프로미스가 만들어지느 그 순간 네트워크 통신을 수행하게 됨
🚨 따라서 네트워크 요청을 사용자가 요구했을 때만 해야하는 경우라면 불필요한 네트워크 통신이 일어날 수 있음!
Consumer: then, catch, finally
위에서 만든 프로미스 사용하기
promise
.then((value) => {
// 이때 value는 프로미스가 resolve()라는 콜백함수로 전달한 데이터
console.log(value);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
// 성공하는 실패하든 실행됨
console.log('finally');
});
Promise chaining
// 서버에서 숫자를 받아오는 새로운 프로미스 생성 (실제로 서버에서 받아오지는 않고, setTimeout을 통해 가정)
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000); // 숫자 1 전달
});
fetchNumber
.then(num => num * 2) // 1 * 2 = 2
.then(num => num * 3) // 2 * 3 = 6
.then(num => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(num - 1), 1000); // 6 - 1 = 5
});
})
.then(num => console.log(num)); // 5
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(friedEgg => console.log(friedEgg));
// 받아온 value를 바로 다음 함수에 파라미터로 전달할 때에는 아래처럼 깔끔하게 표현 가능
getHen()
.then(getEgg)
.then(cook)
.then(console.log); // 🐓 => 🥚 => 🍳
암탉으로부터 달걀을 받아오다가 문제가 생긴다면 다른 재료로 대체를 하고 싶다면? (error handling)
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = hen =>
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000); // error 발생
});
const cook = egg =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
// error handling X
getHen()
.then(getEgg)
.then(cook)
.then(console.log)
.catch(console.log); // Error: error! 🐓 => 🥚
// error handling O
getHen()
.then(getEgg)
.catch(error => {
return '🥐';
})
.then(cook)
.then(console.log) // 🥐 => 🍳
.catch(console.log);
이전에 만든 콜백지옥을 프로미스를 이용해 변경하기
😈 이전 코드
class UserStorage{
loginUser(id, password, onSuccess, onError){
setTimeout(() => {
if((id === 'ann' && password === 'hello') || (id === 'coder' && password === 'academy')){
onSuccess(id);
}else{
onError(new Error('not found'));
}
}, 2000);
}
getRoles(user, onSuccess, onError){
setTimeout(() => {
if(user === 'ann'){
onSuccess({name: 'ann', 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 === 'ann' && password === 'hello') || (id === 'coder' && password === 'academy')){
resolve(id);
}else{
reject(new Error('not found'));
}
}, 2000);
});
}
getRoles(user){
return new Promise((resolve, reject) => {
setTimeout(() => {
if(user === 'ann'){
resolve({name: 'ann', 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);
📁 참고
💛 개인 공부 기록용 블로그입니다. 👻