IT/언어

[JavaScript] Promise의 사용법 ( + asycn / await의 사용법 )

개발자 두더지 2023. 2. 5. 16:24
728x90

일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 직역, 의역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.

 

 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에 문제가 되는 에러 오브젝트 등을 전달

예 : 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()

참고자료

https://qiita.com/suin/items/97041d3e0691c12f4974

728x90