※ 일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 직역, 의역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.
JavaScript의 async/await는 Promise를 사용한 비동기 코드를 동기 처리처럼 해주지만, async/await를 이해하기 위해서는 Promise를 먼저 알아둬야할 필요가 있다. 따라서 Promise에 대해서 알아보자.
구문
먼저 기본적인 구문이다. 어떻게 Promise를 작성하는 것일까?
function 비동기함수(성공시의 콜백, 실패시의 콜백) {
if (...) {
성공시의 콜백(성과)
} else {
실패시의 콜백(문제)
}
}
// ↓executor
new Promise(function (resolve, reject) {
비동기 함수(
(성과) => resolve(성과), // 성공시의 콜백함수
(문제) => reject(문제), // 실패시의 콜백함수
)
})
- Promise 클래스를 new하여 사용
- 컨스트럭터의 인수
- 인수는 executor 1개만
- executor는 Function형
- resolve는 executor에 전달하는 함수
- 비동기 처리가 전달했을 때, resolve에 성공된 값을 전달
- reject는 executor에 전달하는 함수
- 비동기 처리가 실패했을 때, reject에 문제가 되는 에러 오브젝트 등을 전달
- resolve는 executor에 전달하는 함수
예 : axios와 Promise
/**
* 서버의 상태 감시. 상태코드를 반환.
* @returns {Promise.<number>}
*/
function getServerStatusCode() {
return new Promise(function(resolve, reject) {
axios
.get("https://httpbin.org/status/200")
.then(response => resolve(response.status))
.catch(error => reject(error.response.status))
});
}
getServerStatusCode()
.then(statusCode => console.log("살아있음", statusCode))
.catch(statusCode => console.error("죽었음", statusCode))
Promise.resolve
new Promise(function (resolve, reject) {
resolve(1)
})
이 코드는 아래의 코드로 바꿔 쓸 수 있다.
Promise.resolve(1)
그러므로, then등과의 메소드 체인도 가능하다.
Promise.resolve(1)
.then(value => {
console.log(value)
})
async와 awsit ; 비동기 처리를 동기 처럼 쓰기
보다시피, 비동기 처리의 결과는 then으로 콜백을 전달하지 않으면 처리할 수 없다. 동기 처리의 코드와 비교해보면 조금 더 수고를 들일 필요가 있다.
/**
* @returns {Promise.<number>}
*/
function 비동기처리() {
return Promise.resolve(1)
}
/**
* @returns {number}
*/
function 동기처리() {
return 1
}
function main() {
console.log(1 + 비동기처리()) // 1[object Promise]
console.log(1 + 동기처리()) // 2
// Promise의 결과 처리는 then이 필요하다.
非同期処理().then(value => console.log(1 + value)) // 2
}
main()
async와 await를 사용하여 동기처리처럼 쓸 수 있게 된다.
- await : Promise의 값이 도출될 때까지 기다림
- async : await 키워드를 사용하고 있는 함수 주변에 붙일 필요가 있다.
/**
* @returns {Promise.<number>}
*/
function 비동기처리() {
return Promise.resolve(1)
}
async function main() {
console.log(1 + 비동기처리()) // 1[object Promise]
console.log(1 + await 비동기처리()) // 2
}
main()
예시 코드는 다음과 같다.
/**
* 서버의 상태 감시. 상태코드를 반환한다.
* @returns {Promise.<number>}
*/
async function getServerStatusCode() {
try {
return (await axios.get("https://httpbin.org/status/500")).status
} catch (error) {
throw error.response.status
}
}
getServerStatusCode()
.then(statusCode => console.log("살아있음", statusCode))
.catch(statusCode => console.error("죽었음", statusCode))
async function는 Promise를 반환한다.
async는 awsit와 세트로 사용할 필요는 없다. 자체 기능은 함수를 Promise 반환하는 함수로 변환시킨다.
/**
* @returns {Promise.<number>}
*/
function getNumber1() {
return Promise.resolve(1)
}
/**
* @returns {Promise.<number>}
*/
async function getNumber2() {
return 1
}
console.log(getNumber1()) // Promise { 1 }
console.log(getNumber2()) // Promise { 1 }
async function 내에 예외를 던지면 어떻게 될까?
async function내에서 예외를 thorw한 경우에도, 어디까지나 함수의 반환값은 Promise형. 예외도 .catch()로 처리할 수 있다.
/**
* @returns {Promise.<void>}
*/
async function throwError() {
throw "err"
}
throwError().catch(console.log) // err로 출력된다.
async function내에서 Promise를 반환하면 어떻게 될까?
반환값이 Promise라면, 그대로 반환된다. 그러므로, Promise가 이중이되는 것은 아니다.
/**
* async function이지만 Promise를 return하는 함수
* @returns {Promise.<number>}
*/
async function promise() {
return Promise.resolve(1)
}
/**
* @returns {Promise.<number>}
*/
async function number() {
return 1
}
promise().then(console.log) // 1
number().then(console.log) // 1
이것은 async function의 성질이라기 보다는 Promise의 성질인듯하다. Promise.resolve를 넣어도 Promise는 다중이 되지 않는다.
console.log(
Promise.resolve(
Promise.resolve(
Promise.resolve(1)
)
)
) //=> Promise { 1 }
finally는 Promise이기도 하다?
async/await라면 finally으로 성공, 실패시 어느쪽이든 공통의 처리를 실행할 수 있다.
/**
* @returns {Promise.<void>}
*/
async function getStatusCode() {
try {
console.log('started')
const response = await axios.get('https://httpbin.org/status/200')
console.log('success', response.status)
} catch (error) {
console.log('error', error.response.status)
} finally {
console.log('finished') // 공통처리
}
}
동일한 것을 Promise로도 할 수 있지 않을까? 결론을 말하자면 안될 것도 없다.
function getStatusCode() {
console.log('started')
return axios.get('https://httpbin.org/status/200')
.then(response => {
console.log('success', response.status)
})
.catch(error => {
console.log('error', error.response.status)
})
.finally(_ => {
console.log('finished') // !?!?
})
Promise.prototype.finally()를 사용하면 된다.
Promise와 코드의 보수성에 나쁜 영향을 끼치지 않을까?
지금까지 봐왔던 샘플 코드에서는 Promise를 사용해서 그렇게까지 보수성을 헤치지 않았다. 그럼, 어떨 때 async & await의 쪽이 코드가 심플해질 것인가? 그것은 2단 이상 Promise의 then가 겹쳐있을 때라고 생각된다.
예로 들자면 다음과 같다.
function main() {
getX().then(x => {
getY().then(y => {
console.log(x + y)
})
})
}
function getX() {
return Promise.resolve(1)
}
function getY() {
return Promise.resolve(2)
}
main()
이것을 async & await로 바꿔쓰면 심플해진다.
async function main() {
const x = await getX()
const y = await getY()
console.log(x + y)
}
async function getX() {
return 1
}
async function getY() {
return 2
}
main()
참고자료
'IT > 언어' 카테고리의 다른 글
[Java] Java에서 쓰는 Builder 패턴 종류 (0) | 2023.02.10 |
---|---|
[JavaScript] Map 오브젝트와 map 메소드의 사용법 (0) | 2023.02.06 |
[JUnit] JUnit5의 테스트 클래스 작성 (0) | 2023.01.31 |
[Java] Stream (filter, map, forEach, reduce등) (0) | 2023.01.30 |
[Java] Optional 사용법 (0) | 2023.01.29 |