본문 바로가기
Programming/자바스크립트

[자바스크립트] 프로미스 - promise chaining

by 코딩하는 랄로 2023. 11. 6.
728x90

프로미스 핸들러

 

[자바스크립트] 프로미스 - Promise Handler

프로미스의 개념과 기본 사용법 [자바스크립트] 프로미스 - 개념 & 기본 사용법 프로미스의 탄생 : 콜백지옥 자바스크립트에서는 서버에서 데이터를 받아오는 등 시간이 걸리는 작업을 처리를

codingralro.tistory.com

 

저번 포스팅에서는 프로미스의 상태에 따라 후속 작업을 다룰 수 있는 프로미스 핸들러에 대해서 다루었다. 프로미스 핸들러에서 중요한 부분은 핸들러가 새로운 프로미스를 반환한다는 점이었는데, 이번 포스팅을 통해서 해당 사실을 통해 어떻게 프로미스 체이닝을 사용하는지에 대해서 알아보자.

 

 

 

프로미스 체이닝 ( promise chaining )

프로그래밍을 하다보면, 체이닝이라는 말을 많이 접하게 된다. 체이닝이란, 메소드가 반환하는 결과값이 갖는 메소드를 또 이어붙여서 사용하는 방법으로 프로미스 또한 이러한 방법을 사용할 수 있다.

 

왜냐하면, 저번 포스팅에서 언급했던 프로미스 핸들러는 새로운 프로미스 객체를 반환하기 때문이다. 프로미스 객체를 리턴하기 때문에 프로미스 객체가 가지는 핸들러 또는 메소드를 이어 붙이는 것이 가능한 것이다.

 

프로미스는 비동기 작업을 처리하는 문법이기 때문에, 프로미스 체이닝을 사용하면 여러 개의 비동기 작업을 순차적으로 수행할 수 있게 되는 것이다.

 

 

.then()

먼저 .then 메소드는 프로미스가 fulfilled 상태가 되었을 때, 실행되는 메소드이다. .then을 이용한 프로미스 체이닝에 대한 간단한 예를 통해, 어떤 식으로 코드가 흐르는지 살펴보자

function promise() {
    return new Promise((resolve, reject) => {
        resolve('javascript')
    });
}

promise()
    .then(value1 => {
        return value1 + ', java';
    })
    .then(value2 => {
        return value2 + ', python';
    })
    .then(value3 => {
        return value3 + ', C++';
    })
    .then(value4 => {
        console.log(value4);
    }) // javascript, java, python, C++

 

해당 코드의 흐름은 다음과 같다.

  • 프로미스 함수 promise() 호출 => new Promise() 생성자를 통해 프로미스 객체를 반환
  • 반환된 프로미스 객체는 fulfilled 상태 ( 'javascript' 문자열을 넘겨준 resolve를 호출하였으므로 )
  • 첫번째 .then() 메소드 호출 : value1은 'javascript' => 'javascript, java' 를 담고 있는 프로미스 객체를 반환
  • 두번째 .then() 메소드 호출 : value2은 ' javascript, java ' => 'javascript, java, python' 를 담고 있는 프로미스 객체를 반환
  • 세번째 .then() 메소드 호출 : value3은 ' javascript, java, python ' => 'javascript, java, python, C++' 를 담고 있는 프로미스 객체를 반환
  • 네번째 .then() 메소드 호출 : value4는 'javascript, java, python, C++' 를 출력

여기서 .then() 메소드는 문자열을 리턴하지만, 프로미스 체이닝을 사용할 수 있는 이유는 프로미스 핸들러가 반환값을 자동으로 프로미스 객체로 감싼 뒤 반환되기 때문이다.

 

 

.catch

 .catch 메소드는 프로미스가 rejected상태가 되었을 때, 실행되는 메소드이다. .catch 을 이용한 프로미스 체이닝에 대한 간단한 예를 통해, 어떤 식으로 코드가 흐르는지 살펴보자

function promise() {
    return new Promise((resolve, reject) => {
        resolve('javascript')
    });
}

promise()
    .then(value1 => {
        return value1 + ', java';
    })
    .then(value2 => {
        
        if(true) { // 임의로 에러 발생
            throw new Error('에러 발생!!')
        }
        
    })
    .then(value3 => {
        return value3 + ', C++';
    })
    .catch((err) => {
        console.log(err);
        return 'javascript'
    })
    .then(value4 => {
        console.log(value4);
    }) 
/*
Error: 에러 발생!!
    at 에러 발생 위치
javascript
*/

 

위의 코드의 흐름은 아래와 같다.

  • 프로미스 함수 promise() 호출 => new Promise() 생성자를 통해 프로미스 객체를 반환
  • 반환된 프로미스 객체는 fulfilled 상태 ( 'javascript' 문자열을 넘겨준 resolve를 호출하였으므로 )
  • 첫번째 .then() 메소드 호출 : value1은 'javascript' => 'javascript, java' 를 담고 있는 프로미스 객체를 반환
  • 두번째 .then() 메소드 호출 : 에러 발생 => 가까운 catch문으로 점프
  • catch 문 호출 : 에러를 출력한 후, 'javascript' 문자열을 프로미스 객체로 감싼 뒤 반환
  • 마지막 .then() 메소드 호출 : value4는 'javascript' => javascript 출력

이 과정을 통해 알 수 있는 점은, 에러가 발생하면 해당 지점 이후에 프로미스 체이닝 중 제일 가까운 catch문으로 넘어가고 catch문 또한 새로운 프로미스 객체를 반환하기 때문에 프로미스 체이닝을 이어갈 수 있다는 점이다.

 

이 때에, catch문이 어떤 프로미스 객체를 리턴하는 지를 잘 알아야 하고, 해당 에러에 맞는 catch문을 수행하기 위해서는 .catch 문을 이어붙이는 위치 또한 중요하다. 이러한 점을 신경쓰지 않고 코딩하게 된다면, 숫자에 대한 에러를 처리해야 하는데 다른 에러를 처리하는 catch문에 점프할 수 있기 때문이다.

 

 

 

.finally

 .finally 메소드는 프로미스의 상태에 관계없이, 무조건 실행되는 메소드이다. .finally를 이용한 프로미스 체이닝에 대한 간단한 예를 통해, 어떤 식으로 코드가 흐르는지 살펴보자

function promise() {
    return new Promise((resolve, reject) => {
        resolve('javascript')
    });
}

promise()
    .then(value1 => {
        return value1 + ', java';
    })
    .then(value2 => {
        
        if(true) { // 임의로 에러 발생
            throw new Error('에러 발생!!')
        }

    })
    .then(value3 => {
        return value3 + ', C++';
    })
    .finally(() => {
        console.log('첫번째 finally 실행')
    })
    .catch((err) => {
        console.log(err);
        return 'javascript'
    })
    .then(value4 => {
        console.log(value4);
    })
    .finally(() => {
        console.log('두번째 finally 실행')
    })
/*
첫번째 finally 실행
Error: 에러 발생!!
    at 에러 발생 위치
javascript
두번째 finally 실행
*/

 

코드의 흐름을 살펴보면 다음과 같다.

  • 프로미스 함수 promise() 호출 => new Promise() 생성자를 통해 프로미스 객체를 반환
  • 반환된 프로미스 객체는 fulfilled 상태 ( 'javascript' 문자열을 넘겨준 resolve를 호출하였으므로 )
  • 첫번째 .then() 메소드 호출 : value1은 'javascript' => 'javascript, java' 를 담고 있는 프로미스 객체를 반환
  • 두번째 .then() 메소드 호출 : 에러 발생
  • catch문으로 점프하기 전  fianlly 호출 => '첫번째 finally 실행' 출력
  • catch문 실행 => 에러를 출력한 후, 'javascript' 문자열을 프로미스 객체로 감싼 뒤 반환
  • 마지막 .then() 메소드 호출 : value4는 'javascript' => javascript 출력
  • 두번째 finally문 실행 : '두번째 finally 실행' 출력

finally() 사용 시에는 주의해야 할 점이 있는데, 바로 finally의 콜백 함수는 어떤 인수도 넘겨 받지 않는 점이다. finally는 프로미스가 이행되었는지 또는 거부되었는지를 판단할 수 없기 때문에 프로미스가 이행 또는 거부 되었을 때에 반한되는 값이 필요하지 않거나 제공할 필요가 없을 때 사용된다.

 

 

 

 

reference : Inpa dev

728x90