Promise

자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 Object이다.
정상적으로 기능이 수행되었다면 성공한 메세지와 함께 처리된 결과값을 전달해주고, 문제가 발생했다면 error를 전달해준다.

📌 Promise의 중요 포인트

  1. state 상태
  2. Producer vs Consumer

Promise의 상태

|상태|설명|
|--|--|
|pending (대기)| 이행하거나 거부되지 않은 초기 상태|
|fulfilled (이행) |연산이 성공적으로 완료|
|rejected (거부)|연산이 실패함|


Producer vs Consumer

Promise는 원하는 기능을 수행해서 해당하는 데이터를 생성하는 Producer, 원하는 데이터를 소비하는 Consumer로 나뉘어진다.


Producer

Promise를 사용할 때 인자로 executor라는 콜백함수를 전달해줘야 하는데 이 콜백함수에는 또 다른 2개의 콜백함수를 받는다.

  • resolve : 기능을 정상적으로 수행해서 마지막에 최종데이터를 전달하는 콜백함수
  • reject : 기능을 수행하다가 중간에 문제가 생기면 호출하게 될 콜백함수

💡 Promise를 생성하는 순간 executor 함수가 바로 실행되니 유의해야한다.

const promise = new Promise((resolve, reject) => {
  //doing some heavy work...
  console.log("doing something...");
  
  setTimeout(() => {
       	resolve('ellie');	//resolve 콜백함수 호출
    	//reject(new Error('no network'));	//에러 발생
  }, 2000);
});

Consumers

Consumers는 then, catch, finally로 값을 받아올 수 있다.

promise.then((value) => {	//value는 resolve 콜백함수에서 전달된 값
	console.log(value);	//ellie
})
.catch(error => {	//체이닝
	console.log(error);	// Promise에서 reject를 반환하면 Error: no network가 출력된다.
})
.finally(() => {	//finally는 무조건 호출된다
	console.log("finally");
});

Promise chaining

const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000);	// 1초 후 1 전달
});

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));	// 2초 후 5 가 출력된다.

Error Handling

const getHen = () => 
    new Promise((resolve, reject) => {
        setTimeout(() => resolve("🐓"), 1000);
    });

const getEgg = hen => 
    new Promise((resolve, reject) => {
        setTimeout(() => resolve(`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));
  • 결과값
    🐓 => 🥚 => 🍳

💡 callback 함수를 전달할때 받아오는 value를 다른 함수 인자로(단, 인자가 한개일 때) 호출하는 경우에 생략가능

/* --- 생략 전 --- */
getHen()
.then(hen => getEgg(hen))   
.then(egg => cook(egg))
.then(meal => console.log(meal));

/* --- 생략 후 --- */
getHen()
.then(getEgg)   //hen을 바로 다음 함수 인자로 넘기기 때문에 생략가능
.then(cook)	//egg를 바로 cook함수 인자로 넘기기 때문에 생략 가능
.then(console.log);	//meal을 바로 출력하기 때문에 생략 가능

그런데 만약 네트워크 문제가 생겨서 🥚을 받아오는데 문제가 생긴다면 어떻게 해야될까?

  • reject

위 코드에서 달걀부분만 resolve가 아닌 reject로 변경해서 실행해보자. reject를 반환하지만 에러핸들링을 따로 해주지 않아서 아래와 같은 exception을 콘솔창에서 확인할 수 있다.

const getHen = () => 
    new Promise((resolve, reject) => {
        setTimeout(() => resolve("🐓"), 1000);
    });

const getEgg = hen => 
    new Promise((resolve, reject) => {
        setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000)	//reject
    });

const cook = egg =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve(`${egg} => 🍳`), 1000);
    });

getHen()
.then(getEgg)  
.then(cook)
.then(console.log);	

  • 에러핸들링

에러 핸들링을 하기 위해 .catch(console.log); 를 추가하고 실행해보면 에러가 잘 잡힌것을 확인할 수 있다.

getHen()
.then(getEgg)  
.then(cook)
.then(console.log)
.catch(console.log);	//catch

만약 달걀을 받아올때 무슨 문제가 생긴다면 준비한 다른 재료(🥩)로 대체를 하고 싶다면 아래와 같이 작성하면 된다.

getHen()
.then(getEgg) 
.catch(error => {
  return '🥩';	// 🥩 로 대체!
})
.then(cook)
.then(console.log)
.catch(console.log);
  • 결과값
    🥩 => 🍳

콜백지옥 수정

이전 포스팅(콜백이해하기)에서 겪었던 콜백지옥을 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(new Error('not found')));
                }
            }, 2000);
        });
    }

    getRoles(user) {    
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if(user === 'ellie') {
                    resolve({name: 'ellie', role: 'admin'});
                } else {
                    reject(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)
.then(userStorage.getRoles)
.then((user) => {
    alert(`Hello ${user.name} , you have a ${user.role}`);
})
.catch(console.log);

훨씬 깔끔해졌다..!! 😏



💕 참고: 드림코딩 by 엘리
profile
꾸준히 공부하려고 노력하고있는 새싹 개발자 Roiana 입니다 😊

1개의 댓글

comment-user-thumbnail
2021년 9월 6일

잘 읽고 갑니다

답글 달기