나는 항상 프로젝트를 하면서 간단한 useEffect, then을 사용하였기 때문에
promise나 async-await을 쓸 일이 별로 없었다.
그래서 이번에 한 번 정리를 하고 가려한다.
드림코딩 앨리님의 강의를 보며 공부했다.
콜백함수란 다른 함수의 매개변수로 쓰여 바로 실행되지 않고 나중에 Call , back하는 함수이다.
대표적인 예로 setTimeout()함수가 있는데, setTimeout()에서 콜백함수와 시간을 매개변수로 지정하면 일정 시간 후에 해당 함수가 실행되어 비동기적 처리가 가능하다.
하지만 콜백함수로만 비동기적 처리를 하게 되면 한 함수내에서 어떤 함수 호출하고 또 그 안에서 다른 함수 호출하고 ... 이런식으로 호출의 깊이가 깊어져 가독성과 디버깅이 힘들어진다. 따라서 더 편한 방법을 주로 사용한다.
promise는 비동기적 처리를 위해 콜백 대신 사용가능한 js의 object이다.
pending (수행중) -> fulfilled / rejected
const promise =new Promise((resolve, reject)=>{
//시간이 걸리는 작업들 (ex.서버통신)
//여기서 resolve, reject함수를 명시해줘야 함
});
이게 가장 기본적인 형태이다. 이런식으로 producer가 Promise를 생성하면
promise
.then(()=>{
});
.catch(()=>{
});
.finally(()=>{
});
consumer는 then, catch, finally를 사용하여 promise의 실행 상태에 따라 조건부코드를 짜면 된다.
콜백지옥을 promise로 해결해보자
1) callback hell
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}`)
},
error=>{
console.log(error);
})
}
)
2) promise
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);
})
}
}
//Promise를 사용하면 콜백함수가 필요 없음(매개변수로 함수를 전달할 필요 없음)
const userStorage=new UserStorage();
const id=prompt('enter your id');
const password=prompt('enter your password');
userStorage
.loginUser(id, password)
.then(user=>UserStorage.getRoles(user))
.then(user=>alert(`hello ${userWithRole.name}`))
.catch(console.log);
훨씬 가독성이 좋아진다.
더 깔끔하게 Promise를 사용하기 위한 것
function fetchUser(){
//네트워크 통신 중... 10초가 걸린다고 가정
return 'ellie';
}
const user=fetchUser();
console.log(user);
//이런식으로 동기적으로 수행을 한다면 비효율적. 10초동안 다른 일을 하는것이 더 효율적일것임
//promise 버전
function fetchUser(){
//이 일이 다 끝나면 then을 바로 실행하도록 하겠다고 약속
return new Promise((resolve, reject)=>{
//이 안에서 resolve, reject를 명시하지 않으면 promise는 계속 pending상태
resolve('ellie');
})
}
const user=fetchUser();
user.then(console.log);
//async 버전
//async만 쓰면 코드블럭이 자동으로 promise가 됨
async function fetchUser(){
//네트워크 통신 중... 10초가 걸린다고 가정
return 'ellie';
}
const user=fetchUser();
user.then(console.log);
이런식으로 async를 사용하면 코드 블럭을 자동적으로 promise로 바꾸어주므로 더욱 간편하게 비동기 코드를 짤 수 있다.
//await
//async가 붙은 함수 내에서만 사용 가능
//promise를 리턴하는 함수를 기다려줌
function delay(ms){
return new Promise(resolve=>setTimeout(resolve, ms));
}
async function getApple(){
await delay(3000);//delay(서버통신)를 기다리겠다.
return 'apple';
}
async function getBanana(){
await delay(3000);//delay를 기다리겠다.
return 'banana';
}
async function pickFruits(){
const applePromise=getApple();
const bananaPromise=getBanana();
const apple=await applePromise;
const banana=await bananaPromise; //await 병렬 처리 (두개 동시에)
return `${apple}+${banana}`;
}
pickFruits()
.then(console.log);
async 와 await을 사용하면 이렇게 동기적으로 코드작성하듯이 비동기 처리를 할 수 있다.
//useful Promise APIs
function pickAllFruits(){
return Promise.all([getApple(), getBanana()])
.then(fruits=>fruits.join('+'));
}
//제일 먼저 받아지는 것만 쓰고 싶을 땐
function pickOnlyOne(){
// 이 배열에 전달된 프로미스들 중 가장 먼저 수행된 애만 전달됨
return Promise.race([getApple(), getBanana()])
}
Promise의 race api를 사용하면 된다.