Promise
객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냄
비유로 이해하기
가정 상황
소비자의 대안
10초에 한 번씩 묻기 (❌)
다 됐나요? 아니요. 다 됐나요? 아니요. 다 됐나요? 네 준비 되었습니다. 가지고 가세요.
가끔 다 됐나요? 물으면 만들다가 실패한 경우도 있을 것. → 그럼 다시 물건을 주문해야 함.
전화 번호 주고 나오기 (⭕)
상점에 주문 후, (상품이 준비되거나 실패하면 알려달라고) 전화번호를 주고 나오는 게 좋음.
소비자
준비되는 동안 다른 작업 가능
상점
번호 기억했다가 상품 준비되거나 실패 했을 때 소비자에게 알림.
→ 이럴 때 사용할 수 있는 것이 Promise
const pr = new Promise((resolve, reject)=>{
// code
});
new Promise
로 생성.
함수를 전달 받음
resolve
성공 시, 실행되는 함수
reject
실패 시, 실행되는 함수
⇒ 콜백함수
(이렇게 어떤 일이 완료된 후 실행되는 함수)
new Promise
생성자가 반환하는 Promise
객체의 프로퍼티
state
, result
성공할 경우
state
pending(대기)
였다가 resolve
가 호출되면(==성공하면) fulfilled
가 됨.
result
resolve함수
로 전달된 값
실패할 경우
state
pending(대기)
였다가 reject
가 호출되면(==실패하면) rejected
가 됨.
result
reject함수
로 전달된 error
주문을 받으면 3초 동안 뭔가를 하고 성공/실패 알림.
3초 걸리도록 setTimeout
이용
성공할 경우
state
pending
이였다가 3초 후 fulfilled
로
result
undefined
였다가 ‘OK’
실패할 경우
state
pending
이였다가 3초 후 rejected
로
result
undefined
였다가 error
이용해서
resolve
와reject
처리 가능
첫 번째 인수
promise
가 이행되었을 때, 실행하는 함수
ex. 예시의 result
에 들어오는 값: ‘OK’
두 번째 인수
거부되었을 때 실행하는 함수
ex. 예시의 err
에 들어오는 값: error
값
예시
지금 resolve
로 실행됨 → 이 상황에서 두 번째 콜백은 실행 안됨.
reject
인(에러 발생) 경우에만 실행
사용법
두 번째로 전달했던 함수를 catch
내부에 기입 (동일하게 동작)
장점
가독성 더 좋음 (catch
로 명확히 구분)
첫 번째 함수의 에러도 잡기 가능
→ catch
문 사용이 훨씬 좋음
예시
왼쪽의 코드는 오른쪽의 코드로 교체 가능.
처리가 완료되면 항상 실행 (이행 / 거부 상관 無 )
유용한 경우
로딩 화면 같은 것을 없앨 때
예시
pr.then(
function(result){}
).catch(
function(err){}
).finally(
function(){
console.log('--- 주문 끝 ---') // 이것 처럼 사용 가능
}
)
resolve
일 경우
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('끝')
})
/*
- 콘솔:
'시작'
'OK'
'끝'
- 설명:
시작이 나오고 1초 후에 OK가 나옴. */
reject
일 경우
const pr = new Promise((resolve, reject)=> {
setTimeout(()=>{
reject(new Error('err..')) // 👈 reject로 바꿔서 작성 하면
}, 1000)
})
(...생략)
/*
- 콘솔:
'시작'
Error: 'err..'
'끝'
- 설명:
1초 있다가 에러 뜸. */
finally
의 ‘끝’
log → 항상 뜸. (이행 / 거부 무관)
depth
가 깊어지면서 계속Callback
을 호출하는 것
Promise
활용 X )총 3개의 상품 주문
// 1번 주문
const f1 = (callback)=>{
setTimeout(function(){
console.log('1번 주문 완료');
callback();
},1000)
};
// 2번 주문
const f2 = (callback)=>{
setTimeout(function(){
console.log('2번 주문 완료');
callback();
},3000)
}
// 3번 주문
const f3 = (callback)=>{
setTimeout(function(){
console.log('3번 주문 완료');
callback();
},2000)
}
// 👇 [사용] 콜백 지옥
console.log('시작');
f1(function(){
f2(function(){
f3(function(){
console.log('끝')
})
})
})
처음 f1
을 실행하는데 callback
에서 함수를 받아야 함.
그런데 1번 주문이 끝나면 2번 주문을 해야 함. → 함수 내부에 f2
가 들어가게 됨.
f2
도 마찬가지로 callback
함수를 받음
→ 내부에 함수가 있어야 함.
→ f2
가 끝나면 f3
를 실행해야 함
f3
도 마찬가지로 callback
함수를 받음
→ 함수가 들어감
끝남
→ ‘끝’
이라고 적어 줌.
잘 동작함.
'시작'
'1번 주문 완료'
'2번 주문 완료'
'3번 주문 완료'
'끝'
Promise
가 계속 연결 되는 것
Promise
활용callback
함수 활용 X- 동작들: 동일
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('끝')
})
f1
을 실행하고 f1
은 Promise
반환
resolve
함수를 실행하면서 넘겨준 값 → 2번에게 넘김.
f2
는 message
를 받아서 log
를 찍고 마찬가지로 promise
를 반환
resolve
함수로 전달한 값을 3번에서 사용 가능
3번도 message
를 받아서 찍고 promise
반환
→ then
사용 가능
마지막으로 넘겨받은 message
→ console
찍음.
에러 처리 & finally
함.
시작
1번 주문 완료
2번 주문 완료
3번 주문 완료
끝
2번 → 실패(
reject
)로 수정
(...생략)
const f2 = (message) => {
console.log(message)
return new Promise((res, rej)=>{
setTimeout(()=>{
rej('xxx') // 👈 수정
}, 3000)
})
}
(...생략)
시작
1번 주문 완료
xxx
끝
finally
실행
- 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후, 혹은 프로미스가 주어지지 않았을 때 이행하는
Promise
를 반환.- 주어진 프로미스 중 하나가 거부하는 경우, 첫 번째로 거절한 프로미스의 이유를 사용해 자신도 거부.
세 상품들
순차적으로 주문 시, 걸리는 시간: 총 6초
동시 주문 시, 걸리는 시간: 3초 (제일 오래 걸리는 상품의 시간)
→ 이 때 사용 하는 것: Promise.all
Promise.all(iterable);
// iterable: Array와 같이 순회 가능한(iterable) 객체.
(...생략(프로미스 체이닝 - 콜백 지옥 예시 문제 - 해결 - 코드))
Promise.all([f1(), f2(), f3()]).then(res => {
console.log(res)
})
/*
- 결과:
undefined (message가 전달된 것이 없어서 나옴)
undefined ( " )
[ '1번 주문 완료', '2번 주문 완료', '3번 주문 완료' ] 👈 */
Promise.all
쓰고 배열로 받음.
배열 내 작업이 완료 되어야 then
부분 실행
결과
Promise
에 넘겨준 값이 배열로 들어옴.
이유: 한꺼번에 시작 후, 모두 이행되면 값 사용 가능하기 때문
측정 방법
console.time
활용
측정 결과
Promise.all
console.time('x')
Promise.all([f1(), f2(), f3()]).then(res => {
console.log(res)
console.timeEnd('x')
})
/*
- 결과:
(...생략)
'x: 3014ms' 👈 약 3초 걸림 */
Promise
(이전 코드)console.time('시작')
f1()
.then(res => f2(res))
.then(res => f3(res))
.then(res => console.log(res))
.catch(console.log)
.finally(()=>{
console.timeEnd('시작')
})
/*
- 결과:
(... 생략)
시작: 6024ms 👈 약 6초 걸림 */
실패했다고 뜨고 어떤 데이터도 얻지 못함.
const f1 = () => {
return new Promise((res, rej)=>{
setTimeout(()=>{
rej(new Error('xx')) // 👈 수정
}, 1000)
})
};
(...생략)
// 결과: Uncaught (in promise) Error: xx
Promise
: 가져간 데이터 순으로 화면에 그리기 가능Promise.all
: “ 불가다 보여주거나 아예 안보여주거나 할 때 사용
(하나의 정보라도 누락되면 페이지를 안 보여줘야 되는 경우)
iterable
안에 있는 프로미스 중에 가장 먼저 완료된 것의 결과값으로 그대로 이행하거나 거부하는Promise
객체를 반환
Promise.all
과 동일. (all
→race
대체)
Promise.race(iterable);
// iterable: Array와 같은 iterable 객체.
all
: 모든 작업이 완료 될 때까지 기다림race
: 하나라도 1등으로 완료되면 끝냄.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('xx'))
}, 3000)
})
}
const f3 = (message) => {
console.log(message)
return new Promise((res, rej)=>{
setTimeout(()=>{
res('3번 주문 완료')
}, 2000)
})
}
console.time('x')
Promise.race([f1(), f2(), f3()]).then(res => { // 👈 race 사용
console.log(res)
console.timeEnd('x')
})
결과
undefined
undefined
1번 주문 완료 👈
x: 1003ms 👈
해설
1번 주문 완료가 나왔고 2번이 reject
를 예정하고 있었지만, 이미 1번이 완료되어서 무시됨.
용량이 큰 이미지를 로딩하는데 그 중에 하나라도 완료되면 그 이미지를 보여줄 때
참고
코딩앙마 강좌_자바스크립트 중급