Promise
자바스크립트 비동기 처리에 사용되는 객체이다.
아래의 그림을 예시로 보자.
방법 1
1. 소비자가 상품을 상점에 주문
2. 소비자가 상품이 완료 되었는지 상점에 질문
-> 준비 되었다면 상품 차지
-> 준비 되지 않았다면 다시 질문
3. 준비 과정에서 실패가 발생할 수 있음 -> 다시 주문
소비자가 일정한 시간을 가지고 지속적으로 물어보는 것은 비효율적이다.
질문 중에 상품 제작에 실패하면 다시 상품을 주문해야 하는 번거로움도 생긴다.
방법 2
1. 소비자가 상품을 상점에 주문 -> 전화번호를 남김
2. 상점은 상품 제작 성공/실패 여부를 소비자에게 알린다.
상점에 주문을 하고 상품이 준비되었거나 실패됐을 때 알려 달라고 전화번호를 준다면,
상점에서 연락이 올 때까지 소비자는 다른 일을 할 수 있다.
방법 2를 프로미스(Promise)
라고 한다.
const pr = new Promise((resolve, reject) => {
// code
});
resolve
: 성공reject
: 실패📍 콜백 함수 (callvack function)
어떠한 일이 완료되고 난 뒤에 실행되는 함수
프로그래밍에서 콜백(callback) 또는 콜백 함수(callback function)는 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있다.
const pr = new Promise((resolve, reject) => {
// code
});
const pr = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('ok')
},3000)
});
const pr = new Promise((resolve, reject) => {
setTimeout(()=>{
reject(new Error('error..'))
}, 3000)
});
pr.then(
function(result){// 이행되었을 때 실행되는 code},
function(err){// 거부 되었을 때 실행되는 code}
)
이를 작성하여 보자.
pr.then(
function(result){
console.log(result + '가지러 가자');
},
function(err){
console.log('다시 주문해 주세요');
}
)
이 외에 catch
와 finally
이 있다.
catch
는 에러가 발생한 경우, 즉rejected
일 때만 사용 가능.
가독성이 더 좋고, 첫번째 함수 실행시 발생하는 에러도 잡아줄 수 있기 때문에catch문
을 사용하는 것이 좋다.
pr.then(
function(result){}
).catch(
function(err){}
)
finally
는 이행/거부 상관 없이 처리가 완료되면 실행된다.
로딩 화면 등을 없앨 때 유용하다.
pr.then(
function(result){}
).catch(
function(error){}
).finally(
function(){
console.log('---주문 끝---')
}
)
const pr = new Promise((resolve,reject)=> {
setTimeout(()=>{
resolve('ok');
},1000);
});
console.log('시작');
pr.then((result)=>{
console.log(result);
})
.catch((err)=>{
console.log(err);
})
.finally(()=>{
console.log('끝');
})
/*
"시작"
// 1초 지나고 싱행
"ok"
"끝"
*/
const pr = new Promise((resolve,reject)=> {
setTimeout(()=>{
reject(new Error("err..."));
},1000);
});
console.log('시작');
pr.then((result)=>{
console.log(result);
})
.catch((err)=>{
console.log(err);
})
.finally(()=>{
console.log('끝');
});
/*
"시작"
// 1초 지나고 싱행
Error: err....
"끝"
*/
resolve, rejected 여부와 상관 없이
finally
는 실행된다.
promise
를 사용하지 않으면 콜백 헬(콜백 지옥)에 빠질 수 있다.
콜백 지옥은 비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제이다.
이러한 코드 구조는 가독성도 떨어지고 로직을 변경하기도 어렵다.
const f1 = (callback) => {
setTimeout(function (){
console.log('1번 주문 완료');
callback();
}, 1000);
};
const f2 = (callback) => {
setTimeout(function(){
console.log('2번 주문 완료');
callback();
}, 3000);
};
const f3 = (callback) => {
setTimeout(function(){
console.log('3번 주문 완료');
callback();
}, 2000);
};
console.log('시작');
f1(function(){
f2(function(){
f3(function(){
console.log('끝');
});
});
});
위 코드를 promise
로 구현해 보자.
const f1 = () => {
return new Promise((res, rej) =>{
setTimeout(() => {
res('1번 주문 완료');
}, 1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
res('2번 주문 완료');
}, 3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
res('3번 주문 완료');
}, 2000);
});
};
console.log('시작');
f1()
.then((res) => f2(res))
.then((res) => f3(res))
.then((res) => console.log(res))
.catch(console.log)
.finally(() => {
console.log('끝');
});
// result //
/*
"시작"
"1번 주문 완료"
"2번 주문 완료"
"3번 주문 완료"
"끝"
*/
f2 가 오류일 때 f3 은 건너 뛰고 finally
함수가 실행된다.
const f1 = () => {
return new Promise((res, rej) =>{
setTimeout(() => {
res('1번 주문 완료');
}, 1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
rej('error');
}, 3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
res('3번 주문 완료');
}, 2000);
});
};
console.log('시작');
f1()
.then((res) => f2(res))
.then((res) => f3(res))
.then((res) => console.log(res))
.catch(console.log)
.finally(() => {
console.log('끝');
});
// result //
/*
"시작"
"1번 주문 완료"
"error"
"끝"
/*
위의 예제 코드에서 Promise.all
로 바꾸어 보자.
한꺼번에 시작하여 모두 이행되면 값을 사용할 수 있다.
console.log('시작');
Promise.all([f1(), f2(), f3()]).then((res) => {
console.log(res);
});
const f1 = () => {
return new Promise((res, rej) =>{
setTimeout(() => {
res('1번 주문 완료');
}, 1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
rej(new Error('error'));
}, 3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
res('3번 주문 완료');
}, 2000);
});
};
Promise.all([f1(), f2(), f3()]).then((res) => {
console.log(res);
});
promise
와 다르게 에러 메시지와 함께 모두 실행되지 않는다.
하나의 정보라도 누락되면 페이지를 보여 주면 안 되는 경우 주로 사용된다.
하나라도 완료되면 끝낸다.
프로미스 중에 가장 먼저 완료된 것의 결과값으로 그대로 이행하거나 거부한다.
Promise.race([f1(), f2(), f3()]).then((res) => {
console.log(res);
});
용량이 큰 이미지를 로딩하는데 그 중 하나라도 완료되면 그 이미지를 보여 줄 때 사용된다.
console.time()
메서드는 타이머를 시작해 작업이 얼마나 걸리는지 추적할 수 있다.
const f1 = () => {
return new Promise((res, rej) =>{
setTimeout(() => {
res('1번 주문 완료');
}, 1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
res('2번 주문 완료');
}, 3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) =>{
setTimeout(() => {
res('3번 주문 완료');
}, 2000);
});
};
console.time('t');
Promise.all([f1(), f2(), f3()]).then((res) => {
console.log(res);
console.timeEnd('t');
});