Promise.try
어느날, 유투브 쇼츠를 보다가 새로운 기능을 발견했습니다. 그 기능은 바로 일반 함수를 프로미스화 하는 기능입니다. 사용법 자체는 굉장히 단순합니다.
Promise.try(someFunction);
Promise.try
함수의 인자로 콜백 함수를 넘겨주면 끝입니다. 이 콜백 함수는 동기 함수일 수도 있고 비동기 함수일 수도 있습니다. 만약 콜백 함수가 인자를 받는다고 한다면, 다음과 같이 인자를 바인딩해줄 수 있습니다.Promise.try(someFunction, arg1, arg2, arg3, ...);
이 함수를 보니, 제가 종종 일반 함수를 지연 평가하기 위해 프로미스로 만들던 기억이 나네요.
const somePromise = new Promise((resolve) => { // ... resolve(someValue); // ... });
이렇게 보니 새로운 문법은 기존 방식의 문법적 설탕인 것 같네요.
그런데 용법은 약간 다릅니다. 전자의 경우에는
someFunction
이 값을 반환해서 함수가 종료된 시점에 이행될 값을 지정하지만, 후자의 경우에는 함수가 종료되기 전 아무때나 이행될 값을 지정할 수 있습니다. 단순한 함수라면 전자의 용법이 깔끔하고 보기 좋고, 그렇지 않다면 후자가 나아 보입니다.TMI) 프로미스화 하는 과정이 하스켈에서 값을 모나드의 세계로 리프팅하는 것과 비슷한 느낌이 드네요.
일반 함수를 프로미스화 하는 다른 방법
MDN 문서를 보다보니 일반 함수를 프로미스화 하는 다른 방법이 있는 것을 알게 되었습니다.
Promise.resolve(value);
value
부분에 표현식을 넣으면 해당 표현식을 "resolve" 한 프로미스로 만들어줍니다. 여기서 "resolve" 라는 단어는 "fulfilled" 혹은 "rejected"와 엄연히 다릅니다.헷갈리는 프로미스의 상태들
"fulfilled" 는 프로미스가 정상적으로 이행된 상태를, "rejected"는 프로미스가 정상적으로 이행되지 못하고 에러가 발생한 상태를 의미합니다.
그리고 프로미스의 평가가 완료되고 값이 결정되어 변하지 않는 상태를 "settled" 라고 합니다. 즉, 이행되거나 거절된 된 프로미스는 "settled" 된 것입니다.

그렇다면 "resolved" 상태는 뭘까요? 이 용어는 흔히 "fulfilled" 와 동일한 의미로 사용되기는 하지만 엄밀히는 다릅니다.
두 가지 예를 들어 두 용어의 차이를 살펴보도록 합시다.
new Promise(resolve => { resolve(42); // Promise는 즉시 fulfilled 상태로 전환됩니다. });
위와 같이 동기적으로 non-thenable 값을 resolve 하면 즉시 fullfilled 상태로 전환됩니다.
const p1 = new Promise((resolve1, reject1) => { setTimeout(() => resolve1("p1 성공"), 1000); }); const p2 = new Promise((resolve2, reject2) => { console.log("p2가 p1을 resolve합니다."); resolve2(p1); // p2의 상태는 p1의 상태를 따르게 됩니다. }); p2.then(result => console.log("p2 결과:", result)); // 출력: // p2가 p1을 resolve합니다. // (1초 후) p2 결과: p1 성공
위 코드에서
p2
프로미스는 p1
프로미스로 "resolved" 되었습니다.p2
는resolve2(p1)
이 호출되는 순간 resolved 상태가 됩니다.- 즉,
p2
는p1
의 결과에 따르도록 미래가 고정되었습니다.
- 하지만
p2
는 아직 pending 상태이며,p2
의 최종 상태는 p1의 최종 상태에 따라 결정됩니다.
- 만약
p1
이 거절된다면,p2
역시 거절됩니다.
이 시나리오에서 resolved는 "이 Promise는 다른 Promise의 상태를 따를 것이며, 더 이상 resolve나 reject를 직접 호출해도 아무런 효과가 없다"는 의미로 사용됩니다. 즉, "잠금(locked-in)" 상태가 된 것입니다.
조금 어지럽죠? 😅
정리하자면 다음과 같습니다.
- pending: 프로미스의 결과가 아직 결정되지 않은 상태.
- fulfilled: 프로미스가 성공적으로 이행된 상태.
- rejected: 프로미스가 성공적으로 이행되지 못해 거부된 상태.
- settled: 프로미스가 이행 혹은 거부되고 값이 결정되어 더이상 변하지 않는 상태.
- resolved: 프로미스의 결과를 결정짓기 위한 과정이 고정된 상태.
사실 이러한 용어의 혼란이 오는 가장 큰 이유는, 프로미스를 이행시키기 위한 콜백의 첫번째 이름을
resolve
로 쓰기 때문이 아닐까 싶습니다.본론으로 돌아와서
본론으로 돌아와서,
Promise.resolve
는 말 그대로 미래가 고정된 프로미스를 반환합니다.Promise.resolve(value);
value
에 non-thenable 값을 전달하면 즉시 이행된 프로미스를 반환하며, thenable 값을 전달하면 새롭게 프로미스로 래핑하지 않고, 인자로 받은 녀석을 그대로 반환합니다.const original = Promise.resolve(33); const cast = Promise.resolve(original); cast.then((value) => console.log(`value: ${value}`)); console.log(`original === cast ? ${original === cast}`); // Logs, in order: // original === cast ? true // value: 33
original
프로미스 객체와 cast
프로미스 객체는 서로 참조가 동일함을 통해 별다른 래핑 과정 없이 그대로 넘겨준다는 것을 알 수 있습니다.여담
25년에 도입된 새로운 Promise 기능을 알아보고자 했는데, 어쩌다보니 프로미스의 상태에 대한 정리도 하게되었네요. 뭐랄까 배보다 배꼽이 커져버린 듯한 느낌... 😅
그래도 이 글을 통해 헷갈렸던 프로미스의 상태를 명확하게 정리하셨으면 좋겠습니다.
참고
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#description
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/try
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve