Promise
promise 객체는 아래와 같은 문법으로 만든다.
let promise = new Promise(function(resolve, reject){
// executor
});
프라미스는 성공 또는 실패만 한다.
new Promise
state: pending
result: undefined
- resolve(value) — 일이 성공적으로 끝난 경우 그 결과를 나타내는 value와 함께 호출
- reject(error) — 에러 발생 시 에러 객체를 나타내는 error와 함께 호출
const pr = new Promise((resolve, reject) => {
// 어떤 일이 완료된 후 실행 되는 함수 : callback
setTimeout(() => {
resolve('ok') //fullfilled
reject(new Error('error..')) //rejected
}, 3000)
})
[resolve·reject 함수 즉시 호출하기]
executor는 대개 무언가를 비동기적으로 수행하고, 약간의 시간이 지난 후에 resolve, reject를 호출하는데, 꼭 이렇게 할 필요는 없습니다. 아래와 같이 resolve나 reject를 즉시 호출할 수도 있다.
let promise = new Promise(function(resolve, reject) {
// 일을 끝마치는 데 시간이 들지 않음
resolve(123); // 결과(123)를 즉시 resolve에 전달함
});
then, catch, finally
promise 객체의 state, result 프로퍼티는 내부에 있다.
then, catch, finally를 사용하면 접근이 가능하다.
[then]
.then은 프라미스에서 가장 중요하고 기본이 되는 메서드이다.
.then의 첫 번째 인수는 프라미스가 이행되었을 때 실행되는 함수이고, 여기서 실행 결과를 받는다.
.then의 두 번째 인수는 프라미스가 거부되었을 때 실행되는 함수이고, 여기서 에러를 받는다.
작업이 성공적으로 처리된 경우만 다루고 싶다면 .then에 인수를 하나만 전달하면 된다.
//then을 이용해서 pr의 인수로 reject와 resolve를 처리할 수 있다.
pr.then(
function (result) { } - 이행되었을 때
function (err) { } - 거부되었을 때
)
[catch]
에러가 발생한 경우만 다루고 싶다면 catch
catch는 .then에 null을 전달하는 것과 동일하게 작동한다.
[fianlly]
try {...} catch {...}에 finally 절이 있는 것처럼, 프라미스에도 finally가 있다.
쓸모가 없어진 로딩 인디케이터(loading indicator)를 멈추는 경우같이, 결과가 어떻든 마무리가 필요하면 finally가 유용하다.
▼ //then과 catch, finally를 사용할 수 있다.
pr.then(
function (result) { }
).catch( //가독성 편으로 좋다.
function (error) { }
).finally(// reject, resolve 처리가 완료되면 항상 실행 됨
function () {
console.log('---종료---')
}
)
[비동기 처리를 위한 콜백 패턴의 단점]
왜 promise를 쓰게됐는지, callback hell의 모습을 살펴보자.
// callback hell
const f1 = (callback) => {
setTimeout(() => {
res('1번 주문 완료')
callback()
}, 1000);
}
const f2 = (callback) => {
setTimeout(() => {
res('2번 주문 완료')
callback()
}, 1000);
}
const f3 = (callback) => {
setTimeout(() => {
res('3번 주문 완료')
callback()
}, 1000);
}
console.log('시작')
f1(function () {
f2(function () {
f3(function () {
console.log('종료')
})
})
})
▼
[Promise chainng]
해당 콜백 헬 로직을 Promise 객체로 바꿔보기
//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번 주문 완료')
}, 1000);
})
}
const f3 = (message) => {
console.log(message)
return new Promise((res, rej) => {
setTimeout(() => {
res('3번 주문 완료')
}, 1000);
})
}
console.log('시작')
f1()
.then(res => f2(res))
.then(res => f3(res))
.then(res => console.log(res))
.catch(console.log)
.finally(() => {
console.log('끝')
})
Promise.all
모두 한꺼번에 이행시킬 수 있다.
프라미스가 이행될 때까지 기다리다 그 결과 값을 담은 배열을 반환하는 메서드이다.
여러개의 프로미스가 모두 resolve됐을 때 바로 다음 로직을 실행하는 경우에 사용한다.
promise.all()을 사용할 때 주의해야할 점은 배열 내 요소 중 어느 하나라도 거부하면 즉시 거부한다.
* 복수의 URL에 request를 보내고, 모든 요청의 응답이 올때 화면을 랜더 해야하는 상황이 그 예시이다.
요청에 필요한 정보를 배열로 저장한 뒤, 그후 해당 정보를 프로미스로 매핑하여 Promise.all()에 입력하는
방법이 자주 쓰인다.
// promise all
console.time('x')
Promise.all([f1(), f2(), f3()]) //세 작업이 모두 완료되어야지만 then부분이 실행됨
.then(res => {
console.log(res)
console.timeEnd('x')
})
Promise.race
Promise.race()는 가장 빨리 응답을 받은 결과값만 resolve한다.
가장 빨리 도착한 promise의 result만이 .then() 구문으로 넘어갈 수 있다.
// promise.race
console.time('x')
Promise.race([f1(), f2(), f3()])
.then(res => {
console.log(res)
console.timeEnd('x')
})
//1번 주문 완료
x: 1.015s
// 차이점: 하나라도 1등으로 완료되면 종료시킴
async, await
async function getName() {
return 'Mike'
}
// 그래서 함수를 호출하고 바로 .then을 붙일 수 있다.
getName().then(name => {
console.log(name) //Mike
})
만약에 함수의 반환값이 Promise일경우에는?
Promise의 값을 그대로 사용한다.
async function getName2() {
return Promise.resolve('Tom')
}
getName2().then(name => {
console.log(name) //Tom
})
그렇게 되면 함수 뒤 then으로 호출이 아닌 catch로 확인 할 수 있다.
async function getName3() {
throw new Error('error...')
}
getName3().catch(err => {
console.log(err) //Error: error...
})
function getName(name) {
return new Promise((resolve, rej) => {
setTimeout(() => {
resolve(name)
}, 1000)
})
}
async function showName() {
const result = await getName('mike')
console.log(result)
}
console.log('시작')
showName()
//시작
//(1초뒤) mike
[Promise로 구현된 함수들을 async await을 이용해보자.]
asycn await 내부에서도 비동기 함수를 병렬로 실행할 수 있다.
result란 변수에 함수안의 데이터들이 기다렸다가 들어가는 상황을 볼 수 있다.
그래서 promise .then구문보다 가독성이 더 좋다.
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번 주문 완료')
}, 1000);
})
}
const f3 = (message) => {
console.log(message)
return new Promise((res, rej) => {
setTimeout(() => {
res('3번 주문 완료')
}, 1000);
})
}
console.log('시작')
async function order() {
const result1 = await f1()
const result2 = await f2(result1)
const result3 = await f3(result2)
console.log(result3)
console.log('끝')
}
order()
//시작
// 1번 주문 완료
// 2번 주문 완료
// 3번 주문 완료
// 끝
[try.. catch]
만일 reject가 있으면? 에러가 난 후 코드가 종료된다.
이럴경우 promise는 catch를 사용했는데 async awiat은 try.. catch문을 사용할 수 있다.
try문을 사용하고 에러가 났을 경우 catch문을 사용한다.
에러를 처리해주고 다음 코드가 실행된다.
...
const f2 = (message) => {
console.log(message)
return new Promise((res, rej) => {
setTimeout(() => {
rej(new Error('err...'))
}, 1000);
})
}
const f3 = (message) => {
console.log(message)
return new Promise((res, rej) => {
setTimeout(() => {
res('3번 주문 완료')
}, 1000);
})
}
console.log('시작')
async function order() {
try {
const result1 = await f1()
const result2 = await f2(result1)
const result3 = await f3(result2)
} catch (e) {
console.log(e)
}
console.log('끝')
}
order()
//시작
//1번 주문 완료
//Error: err...
//끝
Promise.all을 사용할 때?
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번 주문 완료')
}, 1000);
})
}
const f3 = (message) => {
console.log(message)
return new Promise((res, rej) => {
setTimeout(() => {
res('3번 주문 완료')
}, 1000);
})
}
console.log('시작')
async function order() {
try {
const result = await Promise.all([f1(), f2(), f3()])
console.log(result)
} catch (e) {
console.log(e)
}
console.log('끝')
}
order()
//시작
//[ '1번 주문 완료', '2번 주문 완료', '3번 주문 완료' ]
//끝
댓글