Promise, async-await

saebyeol·2022년 2월 14일
0

나는 항상 프로젝트를 하면서 간단한 useEffect, then을 사용하였기 때문에
promise나 async-await을 쓸 일이 별로 없었다.
그래서 이번에 한 번 정리를 하고 가려한다.

드림코딩 앨리님의 강의를 보며 공부했다.

먼저 자바스크립트는 기본적으로 동기적(synchronous)이다.

  • hoisting: var, function등의 선언이 자동적으로 제일 먼저 실행되는 것

하지만 비동기적으로 코드를 짜기 위한 몇가지 방법이 있는데 가장 기본적인 방법이 콜백함수이다.

1. Callback

콜백함수란 다른 함수의 매개변수로 쓰여 바로 실행되지 않고 나중에 Call , back하는 함수이다.
대표적인 예로 setTimeout()함수가 있는데, setTimeout()에서 콜백함수와 시간을 매개변수로 지정하면 일정 시간 후에 해당 함수가 실행되어 비동기적 처리가 가능하다.

하지만 콜백함수로만 비동기적 처리를 하게 되면 한 함수내에서 어떤 함수 호출하고 또 그 안에서 다른 함수 호출하고 ... 이런식으로 호출의 깊이가 깊어져 가독성과 디버깅이 힘들어진다. 따라서 더 편한 방법을 주로 사용한다.

2. Promise

promise는 비동기적 처리를 위해 콜백 대신 사용가능한 js의 object이다.

1) state

pending (수행중) -> fulfilled / rejected

2) producer vs consumer

const promise =new Promise((resolve, reject)=>{
 	//시간이 걸리는 작업들 (ex.서버통신)
   //여기서 resolve, reject함수를 명시해줘야 함
});

이게 가장 기본적인 형태이다. 이런식으로 producer가 Promise를 생성하면

promise
	.then(()=>{
	});
	.catch(()=>{
    });
	.finally(()=>{
    });

consumer는 then, catch, finally를 사용하여 promise의 실행 상태에 따라 조건부코드를 짜면 된다.

  • promise chaining
    : then 을 여러줄 나열하면 순차적으로 실행된다. 이를 chaining이라 한다.

- exercise) callback to 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);

훨씬 가독성이 좋아진다.

3. async-await

더 깔끔하게 Promise를 사용하기 위한 것

1. Promise -> async


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로 바꾸어주므로 더욱 간편하게 비동기 코드를 짤 수 있다.

2. await

//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을 사용하면 이렇게 동기적으로 코드작성하듯이 비동기 처리를 할 수 있다.

3. useful Promise APIs

  • 위의 코드에서는 apple과 banana get을 동시에 실행하도록 병렬처리를 해주었는데 이 경우 더 간단하게 병렬처리 코드를 짤 수 있는 방법이 있다.
//useful Promise APIs
function pickAllFruits(){
    return Promise.all([getApple(), getBanana()])
    .then(fruits=>fruits.join('+'));
}
  • 만약 바나나와 사과 중 더 먼저 수행이 끝나는 애만 쓰고 싶다면
//제일 먼저 받아지는 것만 쓰고 싶을 땐
function pickOnlyOne(){
    // 이 배열에 전달된 프로미스들 중 가장 먼저 수행된 애만 전달됨
    return Promise.race([getApple(), getBanana()])
}

Promise의 race api를 사용하면 된다.

profile
프론트엔드 개발자

0개의 댓글