Promise.try()
Limited availability
This feature is not Baseline because it does not work in some of the most widely-used browsers.
Promise.try()
는 반환값이나 예외 발생, 동기나 비동기에 관계 없이 모든 종류의 콜백을 가지고, 그 결과를 Promise
내부에서 감싸는 정적 메서드입니다.
구문
Promise.try(func)
Promise.try(func, arg1)
Promise.try(func, arg1, arg2)
Promise.try(func, arg1, arg2, /* …, */ argN)
파라미터
func
-
주어진 인자 (
arg1
,arg2
, …,argN
) 를 동기적으로 호출되는 함수입니다. 이는 반환값이든, 예외를 발생시키든, 프로미스를 반환하든 그 어떤 것이든 수행할 수 있습니다. arg1
,arg2
, …,argN
-
func
에 전달되는 인자입니다.
반환 값
Promise
는 다음 값들을 반환합니다.
func
가 동기적으로 값을 반환한다면 이미 fulfilled 상태입니다.func
가 동기적으로 예외를 발생시킨다면 이미 rejected 상태입니다.func
이 프로미스를 반환한다면 비동기적으로 fulfilled 혹은 rejected 상태입니다.
설명
콜백을 취하는 API가 있다고 가정하겠습니다. 이 콜백은 동기적으로 동작할 수 있고 비동기로 동작할 수도 있습니다. 결과를 프로미스로 감싸서 모든 함수를 동일하게 동작하게 하고 싶을 때 가장 간단한 방법은 Promise.resolve(func())
를 사용하는 것일 수 있습니다. 만일 func()
가 동기적으로 예외를 발생시킨다면 이 예외는 감지되지 않고 rejected 프로미스로 변환되지 않는다는 문제가 있을 수 있습니다.
일반적으로 함수를 fulfilled나 rejected 상태의 프로미스로 끌어올리기 위한 접근 방법은 아래와 같습니다.
new Promise((resolve) => resolve(func()));
여기서는 Promise.try()
가 더욱 효과적입니다.
Promise.try(func);
내장 Promise()
생성자는 실행자로부터 발생된 예외를 자동적으로 감지하고 거부됨으로 반환합니다. 따라서 이 두 접근 방법은 유사하지만 Promise.try()
가 더 간단하고 가독성이 좋습니다.
Promise.try()
가 이와 완전히 동일한 것이 아니라, 상당히 유사하다는 점을 기억해야 합니다.
Promise.resolve().then(func);
then()
에 전달된 콜백은 항상 동기적으로 실행되는 Promise()
생성자와는 다르게 항상 비동기적으로 호출된다는 점입니다. Promise.try
는 함수를 동기적으로도 호출하고 가능하다면 즉시 프로미스를 이행 상태로 만듭니다.
catch()
와 finally()
가 결합된 Promise.try()
는 동기와 비동기 처리를 하나로 묶어 처리할 수도 있습니다. 그리고 프로미스의 예외 처리를 마치 동기 예외 처리와 비슷한 것처럼 보이게 할 수 있습니다.
setTimeout()
처럼, Promise.try()
는 콜백으로 전달되는 추가적인 인자들도 수용합니다. 이를 이해하기 위해 아래 예제를 확인해 보세요.
Promise.try(() => func(arg1, arg2));
이렇게도 작성할 수 있습니다.
Promise.try(func, arg1, arg2);
이들은 동일한 동작을 하지만 후자는 추가적인 클로저 생성을 방지할 수 있어 더 효율적입니다.
예제
Promise.try() 사용하기
다음 예제는 콜백을 받아서 프로미스로 끌어올리고, 그 결과를 처리하며 오류 처리도 수행합니다.
function doSomething(action) {
return Promise.try(action)
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log("Done"));
}
doSomething(() => "Sync result");
doSomething(() => {
throw new Error("Sync error");
});
doSomething(async () => "Async result");
doSomething(async () => {
throw new Error("Async error");
});
async/await 구문에서는 같은 코드를 이렇게 표현할 수도 있습니다.
async function doSomething(action) {
try {
const result = await action();
console.log(result);
} catch (error) {
console.error(error);
} finally {
console.log("Done");
}
}
프로미스가 아닌 생성자에서 try() 호출하기
Promise.try()
는 제너릭 메서드입니다. 이는 Promise()
생성자와 동일한 시그니처를 구현하는 모든 생성자에서 호출될 수 있습니다.
다음은 실제 Promise.try()
의 좀 더 충실한 근사치입니다. (하지만 폴리필로 사용해서는 안 됩니다.)
Promise.try = function (func) {
return new this((resolve, reject) => {
try {
resolve(func());
} catch (error) {
reject(error);
}
});
};
Promise.try()
의 (try...catch
를 이용한 것과 같은) 구현 방식 덕분에 어떤 커스텀 생성자에 this
를 설정하여 Promise.try()
를 안전하게 호출할 수 있고, 절대로 동기적으로 예외를 발생시키지 않습니다.
class NotPromise {
constructor(executor) {
// "resolve" 와 "reject" 함수는 어떠한 동작도 하지 않습니다.
// 그러나 프로미스의 Promise.try() 는 resolve 를 호출합니다.
executor(
(value) => console.log("Resolved", value),
(reason) => console.log("Rejected", reason),
);
}
}
const p = Promise.try.call(NotPromise, () => "hello");
// Logs: Resolved hello
const p2 = Promise.try.call(NotPromise, () => {
throw new Error("oops");
});
// Logs: Rejected Error: oops
Promise()
와는 다르게 NotPromise()
생성자는 실행자가 동작하는 동안 어떠한 예외도 우아하게 처리하지 않습니다. 하지만 throw
와는 다르게 Promise.try()
는 예외를 감지하고, 이를 reject()
로 전달하여 로그를 출력하게 됩니다.
명세서
Specification |
---|
Promise.try # sec-promise.try |
브라우저 호환성
BCD tables only load in the browser