javascript

[javascript] Promise then/catch/finally

잘할수있을거야 2023. 10. 1. 18:38

 

Uncaught (in promise)

 

promise가 성공하는 경우 핸들링하지 않해도 문제가 없지만,

promise가 실패하는 경우 핸들링하지 않으면 Uncaught (in promise)가 발생한다.

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise</title>
    <link rel="icon" href="data:,">
</head>

<body>
    <button onclick="checkPromise()">Promise 상태 확인</button>
    <script>
        let promise;

        function create(success, second) {
            promise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (success) {
                        console.log('promise 성공시 호출됨')
                        resolve('promise성공시 데이터!!!')
                    } else {
                        console.log('promise 실패시 호출됨')
                        reject(new Error('promise실패시 이유!!!'))
                    }
                }, 1000 * second)
            })
        }

        function checkPromise() {
            console.log('--- Promise check ---')
            console.log('promise: ', promise)
            console.log('---------------------')
        }
    </script>
</body>

</html>

create 호출 후 -> Promise 상태 확인 클릭 - > Promise가 완료(성공/실패)된 후 Promise 상태 확인 클릭

(이후의 모든 예시에서 위와 같은 형태로 클릭하여 진행한다)

 

Promise가 성공하는 경우

 

Promise가 실패하는 경우

promise 실패시 실패에 대한 핸들링이 없는 경우 Uncaught (in promise)가 발생


Promise.then

onRejected는 optional로 선언시 promise의 실패시 에러를 핸들링 할 수 있다.

 

promise.then(onFulfilled, onRejected) 

onFulfilled는 promise의 성공을 처리하는 핸들러

onRejected는 promise의 실패를 처리하는 핸들러

onFulfilled, onRejected 둘 다 넘기는 경우 promise의 성공/실패를 모두 핸들링할 수 있게된다.

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise</title>
    <link rel="icon" href="data:,">
</head>

<body>
    <button onclick="checkPromise()">Promise 상태 확인</button>
    <script>
        let promise;
        let thenPromise;

        function create(success, second) {
            promise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (success) {
                        resolve('promise성공시 데이터!!!')
                    } else {
                        reject(new Error('promise실패시 이유!!!'))
                    }
                }, 1000 * second)
            })
            // fulfilled, rejected 콜백 모두 존재하는 경우
            thenPromise = promise.then(
                value => {
                    console.log('onFulfilled (promise 성공시 호출)')
                    console.log(`promise 성공값: ${value}`)
                },
                error => {
                    console.log('onRejected (promise 실패시 호출)')
                    console.log(`promise 실패에러: ${error}`)
                }
            )
        }

        function checkPromise() {
            console.log('--- Promise check ---')
            console.log('promise: ', promise)
            console.log('thenPromise: ', thenPromise)
            console.log('---------------------')
        }
    </script>
</body>

</html>

 

promise가 성공하는 경우

create(true, 3)

promise가 성공했기 때문에

-> onRejected, onFulfilled중에 onFulfilled 핸들러가 호출된다

-> onFulfilled 핸들러가 리턴하는 순간 thenPromise는 pending -> fulfilled상태가 되며 값은 onFulfilled의 리턴값(Promise가 아닌 경우)을 가진다.

 

코드를 수정하여 then의 onFulfilled 콜백에 Promise가 아닌값을 리턴하게 변경한 후 동일하게 create(true, 3)하면

            // fulfilled, rejected 콜백 모두 존재하는 경우
            thenPromise = promise.then(
                value => {
                    console.log('onFulfilled (promise 성공시 호출)')
                    console.log(`promise 성공값: ${value}`)
                    return 'thenPromise 성공값 1' // Promise가 아닌 값 명시적 리턴
                },
                error => {
                    console.log('onRejected (promise 실패시 호출)')
                    console.log(`promise 실패에러: ${error}`)
                }
            )

 

thenPromise의 fulfilled의 값이 onFulfilled 핸들러의 리턴값인 것을 확인할 수 있다.

 

변경한 return값은 그대로 두고 이후의 예제들을 진행하자


promise가 실패하는 경우

create(false, 3)

promise가 실패했기 때문에

-> onRejected, onFulfilled중에 onRejected 핸들러가 호출된다

-> onRejected 핸들러가 리턴하는 순간 thenPromise는 pending -> fulfilled상태가 되며 값은 onRejected의 리턴값(Promise가 아닌 경우)을 가진다.

 

코드를 수정하여 then의 onRejected콜백에 Promise가 아닌값을 리턴하게 변경한 후 동일하게 create(false, 3)하면

            // fulfilled, rejected 콜백 모두 존재하는 경우
            thenPromise = promise.then(
                value => {
                    console.log('onFulfilled (promise 성공시 호출)')
                    console.log(`promise 성공값: ${value}`)
                    return 'thenPromise 성공값 1' // Promise가 아닌 값 명시적 리턴
                },
                error => {
                    console.log('onRejected (promise 실패시 호출)')
                    console.log(`promise 실패에러: ${error}`)
                    return 'thenPromise 성공값 2' // Promise가 아닌 값 명시적 리턴
                }
            )

 

thenPromise의 fulfilled의 값이 onRejected 핸들러의 리턴값인 것을 확인할 수 있다.

 

변경한 return값은 그대로 두고 이후의 예제들을 진행하자


promise.then(onFulfilled)

 

onRejected 인자를 넘기지 않은 경우 promise 성공시 promise.then(onFulfilled, onRejected)와 같은 동작을 하고 실패시에만 동작이 달라진다.

 

코드에서 다음과 같이 onRejected 콜백을 제거하여 진행하자.

            // fulfilled 콜백만 존재하는 경우
            thenPromise = promise.then(
                value => {
                    console.log('onFulfilled (promise 성공시 호출)')
                    console.log(`promise 성공값: ${value}`)
                    return 'thenPromise 성공값 1' // Promise가 아닌 값 명시적 리턴
                }
                // onRejected 콜백 제거
            )

 

Promise가 성공하는 경우

create(true, 3)

성공시에는 then(onFulfilled, onRejected)와 동일한 결과가 나온다.

하지만 바로 다음의 promise가 실패할 때는 결과가 다르다.

 

Promise가 실패하는 경우

create(false, 3)

promise.then(onFullfilled, onRejected)의 경우에는 promise가 실패할 때

실패 핸들러 onRejected가 정의되어 있어 실패한 promise를 핸들링하였지만

 

promise.then(onFulfilled)의 경우에는 promise가 실패할 때

실패 핸들러가 없어 promise의 실패를 처리하지 못했기 때문에 글의 초반부에 설명한 Uncaught (in promise)가 발생하고

thenPromise는 rejected 프로미스가 된다.


Promise.catch

 

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise</title>
    <link rel="icon" href="data:,">
</head>

<body>
    <button onclick="checkPromise()">Promise 상태 확인</button>
    <script>
        let promise;
        let catchPromise;

        function create(success, second) {
            promise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (success) {
                        resolve('promise성공시 데이터!!!')
                    } else {
                        reject(new Error('promise실패시 이유!!!'))
                    }
                }, 1000 * second)
            })
            catchPromise = promise.catch(
                error => {
                    console.log('onRejected (promise 실패시 호출)')
                    console.log(`promise 실패에러: ${error}`)
                    return 'catchPromise 성공값' // Promise가 아닌 값 명시적 리턴
                }
            )
        }

        function checkPromise() {
            console.log('--- Promise check ---')
            console.log('promise: ', promise)
            console.log('catchPromise: ', catchPromise)
            console.log('---------------------')
        }
    </script>
</body>

</html>

 

Promise가 성공하는 경우

create(true, 3)

onRejected핸들러는 호출되지 않으며 catchPromise는 원 promise의 fulfiilled 값을 가진 fulfilled 프로미스가 된다.

 

Promise가 실패하는 경우

create(false, 3)

onRejected핸들러가 호출되며 catchPromise는 onRejected의 리턴값을 가진 fulfilled 프로미스가 된다.


promise.finally

 

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise</title>
    <link rel="icon" href="data:,">
</head>

<body>
    <button onclick="checkPromise()">Promise 상태 확인</button>
    <script>
        let promise;
        let finallyPromise;

        function create(success, second) {
            promise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (success) {
                        resolve('promise성공시 데이터!!!')
                    } else {
                        reject(new Error('promise실패시 이유!!!'))
                    }
                }, 1000 * second)
            })

            finallyPromise = promise.finally(() => {
                console.log('promise 성공/실패시 호출')
            })
        }

        function checkPromise() {
            console.log('--- Promise check ---')
            console.log('promise: ', promise)
            console.log('finallyPromise: ', finallyPromise)
            console.log('---------------------')
        }
    </script>
</body>

</html>

 

Promise 성공시

finallyPromise는 promise의 fulfilled 값을 가진 fulfilled 프로미스가 된다.

 

Promise 실패시

.catch로 실패한 promise를 처리하지 않은 상태에서 finally는 promise의 rejected값을 가진 rejected 프로미스가 된다.


.catch(onRejected)는 내부적으로 .then(undefined, onRejected)를 호출한다.

 

create는 then(undefined, onRejected)을 통해 실패를 핸들링한 경우

create2는 catch(onRejected)를 통해 실패를 핸들링한 경우

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise</title>
    <link rel="icon" href="data:,">
</head>

<body>
    <button onclick="checkPromise()">Promise 상태 확인</button>
    <script>
        let promise;
        let secondPromise;

        // promise.then(undefined, onRejected)
        function create(success, second) {
            promise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (success) {
                        resolve('promise성공시 데이터!!!')
                    } else {
                        reject(new Error('promise실패시 이유!!!'))
                    }
                }, 1000 * second)
            })
            secondPromise = promise.then(undefined, error => {
                console.log('onRejected (promise 실패시 호출)')
                console.log(`promise 실패에러: ${error}`)
                return 'secondPromise 성공값'
            })
        }

        // promise.catch(onRejected)
        function create2(success, second) {
            promise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (success) {
                        resolve('promise성공시 데이터!!!')
                    } else {
                        reject(new Error('promise실패시 이유!!!'))
                    }
                }, 1000 * second)
            })
            secondPromise = promise.catch(error => {
                console.log('onRejected (promise 실패시 호출)')
                console.log(`promise 실패에러: ${error}`)
                return 'secondPromise 성공값'
            })
        }

        function checkPromise() {
            console.log('--- Promise check ---')
            console.log('promise: ', promise)
            console.log('secondPromise: ', secondPromise)
            console.log('---------------------')
        }
    </script>
</body>

</html>

 

Promise가 성공한 경우

onFulfilled이 undefined인 경우 원 promise의 fulfilled된 값을 가지는 fulfilled 프로미스가 된다

 

then(undefined, onRejected)와 결과가 동일한 것을 확인 가능

 

Promise가 실패한 경우

위에서 이미 살펴봤다.