callback을 대체하여 비동기를 간편하게 처리할 수 있도록 도와주는 object.
정해진 장시간의 기능을 시행하고나서 정상적으로 동작했다면 성공 메시지 전송, 실패했다면 에러 메시지 전송.
promise는 두가지 포인트만 알고 가면 이해하기 편하다
- state : 시행하고 있는 중인지, 성공했는지 실패했는지의 상태 (pending -> fulfilled or rejected)
- producer & consumer : 정보를 제공하는 producer(promise 생성) 소비하는 consumer(promise 실행)로 나눌 수 있다.
Producer
새로운 Promise는 생성되는 순간, 안에 있는 executer callback function을 자동으로 실행하게 된다.
See the Pen Promise by zakelstorm (@zakelstorm) on CodePen.
위의 상황에서 먼저 첫번째 경우를 보면 다음과 같은 특징을 가지고 있다.
- new Promise가 선언됨 시점에서 Promise내부의 callback함수가 실행됨.
- promise.then을 3번 호출해도 Promise내부의 callback함수가 실행되지 않음.
이와 같은 특징에서
Promise는 외부에서 선언과 동시에 callback 함수가 실행이 되며
이 Promise 선언을 대입받은 변수(promise)는 Promise가 resolve 한 값을 받게 되어 해당 변수에 then을 붙여 실행하여도 기다림 없이 then의 callback함수를 실행하게 된다.
이를 사용자가 원하는 시점에 Promise를 실행하고 원하는 then callback함수 실행 시점을 만들기 위해
코드의 두 번째 경우에서 처럼 Promise를 함수에 감싸 함수 실행하듯 사용하면 위의 문제를 해결할 수 있다.
Consumer
실행된 promise에 대해 then, catch, finally를 이용하여 resolve 또는 reject 된 값을 가져올 수 있다.
resolve와 reject는 각각 then, catch에 대응되며 사용방법은 다음과 같다.
const producer = new Promise((resolve,reject)=>{
console.log('doing someting...')
setTimeout(()=>{
resolve('success');
},2000)
})
producer.then(value=>{
console.log(value); // resolve value인 'success'를 출력
})
const producer2 = new Promise((resolve,reject)=>{
console.log('doing someting...')
setTimeout(()=>{
reject('fail');
},2000)
})
producer2.then(value=>{
console.log('then') //성공적인 case만 출력하므로 then구문은 타게되지않는다.
console.log(value);
}).catch(value=>{
console.log('catch') // reject되는 순간 catch 구문을 타게된다.
console.log(value);// resolve value인 'fail'를 출력
})
then은 promise를 반환하기 때문에 이 뒤에 catch 또는 then을 또 한 번 호출할 수 있다. 이를 chaining이라고 한다.
Promise chaining
const fetchNumber = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1),1000
})
})
fetchNumber
.then(num => num*2) // 2, num*2 앞에 (return)이 생략되어 있다
.then(num=>num*5) // 10
.then(num=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
num-=7;
resolve(num); // 3
},1000)
})
})
.then(num=>{
console.log(num) // 3
})
;
then은 일반적인 return 또는 promise resolve를 바로 전달할 수 있다. (then 또한 Promise 값을 return 하기 때문)
이러한 점덕분에 위 코드에서 (return) num 또는 resolve(num) 값이 뒤의 then으로 넘어간 것이다.
Error Handling
const getHen = () =>{
new Promise((resolve,reject)=>{
setTimeout(()=>resolve('myhen'),2000);
});
}
const getEgg = (hen) =>{
new Promise((resolve,reject)=>{
setTimeout(()=>reject(`${hen} => myegg`),2000);
});
}
const cook = (egg) =>{
new Promise((resolve,reject)=>{
setTimeout(()=>resolve(`${egg} => myfried egg`),2000);
});
}
// return값을 그대로 한개의 함수로 바로 넘긴다면 다음과 같이 parameter생략가능
getHen
.then(getEgg)
.then(cook)
.then(console.log) //어떠한 것도 출력되지않음
getHen
.then(getEgg)
.then(cook)
.then(console.log)
.catch(console.log) // Error메시지와 함께 myhen => myegg 출력
//전체적인 promise chain에 문제가 생기지 않도록
getHen
.then(getEgg)
.catch(error =>
return 'mybread'
)
.then(cook)
.then(console.log) // mybread => myfried egg 출력
.catch(console.log)
catch는 이전의 Promise chain에서 reject 반환 시 실행되는 구문이다.
javascript에서는 catch의 위치를 적절히 대입하여 error에 대응할 수 있다.
catch 구문 뒤의 then은 앞의 catch 구문에서 에러를 처리한 후에도 계속 이어서 실행되며,
catch 뒤의 catch의 실행 시점은 앞의 catch와 뒤의 catch사이에서 error가 생겨야 비로소 실행이 된다.
Callback 지옥 해결
이전의 Callback지옥을 해결한 버전은 다음과 같다. then을 중첩해서 쓰는 과오를 범했었는데 then을 중첩해서 쓰면 callback지옥과 결국 비슷한 의미가 된다. 가능한 떨어뜨리도록 하자.
See the Pen VwKrEOw by zakelstorm (@zakelstorm) on CodePen.