Published on

Promise.resolve는 성공이 아니다

Authors

    You don't know JS에서 프라미스 파트를 읽으면서, 프라미스에 대해 새로운 것들을 알게 되었다. 그 중에서 Promise.resolve가 실패하는 케이스를 보게 되었다. 그 예제를 통해서, 그동안 내가 Promise.resolve를 성공이라고 잘못 알고 있던 것을 알게 되었다. 이번 글에선 Promise.resolve가 진정 무엇인지 정리해본다.

    Promise.resolve는 항상 '성공'일까?

    프라미스를 설명하는 글을 읽으면 여러 가지 용어가 나온다. 특히 resolve, fulfilled, rejected, pending, settled가 많이 보일 것이다. 나는 resolve를 성공이라는 의미로 생각하였고, resolve랑 fulfilled는 둘 다 '성공'을 의미한다고 생각했다.

    아래 예시를 먼저 살펴보자.

    const promise = new Promise((resolve, reject) => {
        resolve('success!');
    });
    
    promise.then(
    	(value) => {console.log(`success!: ${value}`)}, // 이 콜백이 호출됨
    	(error) => {console.log(`error!: ${error}`)},
    );
    
    // success!: success!
    

    프라미스의 then 메서드는 프라미스가 성공했을 때는 첫 번째 콜백 함수를 호출하고, 실패했을 때는 두 번째 콜백 함수를 호출한다.

    .then(
    	(value) => { /* 성공 핸들러 */ }, 
    	(error) => { /* 실패 핸들러 */ },
    );
    

    위 예시에서 resolve('success!')를 호출하면, .then의 첫 번째 콜백인 성공 핸들러가 호출된 것을 확인할 수 있다. 그렇다면, "resolve를 호출하면, 성공이 된다!" 고 봐도 되지 않을까?

    하지만 아래 예제를 보면, 좀 헷갈린다. resolve 함수에 Promise.reject를 건네면, 그 프라미스는 성공일까 실패일까?

    const promise = new Promise((resolve, reject) => {
      resolve(Promise.reject('Oops! ❌'))
    })
    
    promise.then(
    	(value) => {console.log(`success!: ${value}`)},
    	(error) => {console.log(`error!: ${error}`)},
    );
    
    

    결과는 then의 두 번째 콜백인 실패 핸들러가 호출된다.

    error!: Oops! ❌
    

    이걸 보면, resolve가 반드시 '성공'으로 이어지지 않음을 알 수 있다.

    Promise.resolve는 무슨 의미일까?

    그렇다면, resolve는 대체 무엇일까? 그 의미를 알기 전에, 프라미스는 어떤 상태일 수 있는지 알아보자.

    프라미스의 상태

    프라미스는 3개의 상태 중 하나일 수 있다. 바로 pending, fulfilled, rejected이다.

    • 프라미스의 상태
      • pending: fulfilled도 아니고, rejected도 아닌 상태
      • fulfilled: 프라미스의 실행이 성공으로 끝난 상태
      • rejected: 프라미스의 실행이 실패로 끝난 상태 Sheep-Screenshot 2023-03-05 PM 1.03.40.png 그림에 보이는 settled는 상태가 아니다. 프라미스의 상태가 pending에서 성공(fulfilled)나 실패(rejected)로 결정이 되면, settled라고 표현한다.

    resolved의 의미

    프라미스는 3개의 상태 중 한 가지 상태일 수만 있다. 그리고 이 상태 중에서 resolved라는 상태는 없다. resolved는 무엇을 의미할까? 프라미스를 제안한 문서 중 States and Fates 에서는 resolved를 이렇게 설명하고 있다.

    A promise is resolved if trying to resolve or reject it has no effect, i.e. the promise has been "locked in" to either follow another promise, or has been fulfilled or rejected.

    해석하면, resolved는 프라미스가 다른 프라미스를 따르기로 했거나, 성공 혹은 실패했을 때를 말한다. resolve되었으면, 거기서 resolve나 reject를 하려고 해도 어떤 영향도 없다.

    resolved는 프라미스가 '성공' 혹은 '실패'의 상태일 때를 말한다. 그런데, 또 다른 설명들이 있다. 바로 다른 프라미스를 따른다는 것resolve, reject를 해도 어떤 영향도 없다는 것이다. 이 두 개는 무엇을 뜻하는지는 예시를 통해 확인해보자.

    resolved 첫 번째 예시

    먼저 프라미스가 다른 프라미스를 따르는 예시는 다음과 같다.

    const fulfilledPromise = Promise.resolve('성공!');
    const rejectedPromise = Promise.reject('실패...');
    
    // 🅰️
    Promise.resolve(fulfilledPromise)
    .then(
    	(value) => {console.log(`1. 성공: ${value}`)},
    	(error) => {console.log(`1. 에러: ${error}`)},
    );
    
    // 🅱️
    Promise.resolve(rejectedPromise)
    .then(
    	(value) => {console.log(`2. 성공: ${value}`)},
    	(error) => {console.log(`2. 에러: ${error}`)},
    );
    
    1. 성공: 성공!
    2. 에러: 실패...
    

    Promise.resolve가 인자로 프라미스를 받으면, 그 프라미스를 리턴한다. 만약 인자가 프라미스도 아니고 데너블(Thenable)도 아니면, 프라미스는 그 값으로 '성공'하게 된다.

    A 구간의 Promise.resolvefulfilledPromise라는 프라미스를 받았기에, fulfilledPromise를 리턴하게 되고, 그 프라미스는 '성공!'이라는 값으로 fulfilled 상태가 된다.

    반면, B 구간의 Promise.resolverejectedPromise를 받았기에, rejectedPromise를 리턴한다. 그리고 그 프라미스는 '실패...'라는 이유로 rejected 상태가 된다.

    A 구간은 성공한 프라미스로 resolved되었고, B 구간은 실패한 프라미스로 resolved되었다.

    resolved 두 번째 예시

    프라미스가 resolved되었을 때, 그 이후의 resolvereject를 해도 어떠한 영향도 없는 예시는 아래와 같다.

    const p1 = new Promise((resolve, reject) => {
        resolve('성공-!');
        reject('실패...'); // 무시됨
      })
    
    p1.then(
      (result) => {console.log(`성공: ${result}`)},
      (error)  => {console.log(`실패: ${error}`)}
    )
    
    성공: 성공-!
    

    p1에서는 먼저 resolve를 호출하였고, resolve 내의 성공-!이라는 값으로 성공의 상태가 된다. 'fulfilled'라는 상태로 resolved되었기에, 그 이후로 reject를 호출하더라도, p1의 상태가 성공에서 실패로 변경되지 않는다. 위 예시에서 reject()를 resolve()보다 먼저 호출하더라도, p1의 상태가 실패에서 성공으로 변경되지 않는다. 즉, 한 번 resolved된 프라미스의 상태는 변경되지 않는다.

    정리

    정리하면, resolvefulfilled 혹은 rejected로 값이 결정되었거나, 다른 프라미스를 따르기로 결정함을 의미한다. 반드시 resolve가 '성공'은 아닌 것이다. 그리고, resolved된 프라미스의 상태는 불변하기에, 다른 상태가 될 수 없다.

    Reference