[javascript] Promise

Roiana·2021년 9월 6일
2
post-thumbnail
post-custom-banner

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 입니다 😊
post-custom-banner

1개의 댓글

comment-user-thumbnail
2021년 9월 6일

잘 읽고 갑니다

답글 달기