then 메소드를 갖는 객체를 뜻한다. 체이닝이나 await과 같은 promise 패턴을 가진 구문에서 사용할 수 있다. 모든 promise객체는 thenable 객체이지만 역은 성립하지 않는다.
const thenable = {
then(onFulfilled, rejected) {
setTimeout(() => {
onFulfilled(42);
}, 10);
},
};
Promise.resolve(1)
.then(() => thenable)
.then(console.log);
const v = await thenable;
console.log('v: ', v);
위의 예시처럼 thenable 객체는 await
를 통해 then() 메소드를 호출하여 값을 받을 수 있다.
const obj = {
then(onFulfilled) {
console.log('inside then method');
console.log(onFulfilled.toString());
},
};
(async () => {
console.log('start');
await obj;
console.log('end');
})();
// output>
// start
// inside then method
// function () { [native code] }
then()
메소드의 인자로 두 가지 콜백이 전달된다. 정상적으로 이행했을 때 실행될 코드(onFulfilled)와 실패했을 때 실행될 콜백(rejected)가 그것이다.
위 예제는 onFulfilled을 전달받아서 호출하지 않았다. 그래서 하단에 await문 이후로 코드가 실행되지 않아 console.log(”end”)
가 실행되지 않았다. 이 코드를 실행되도록 바꾸면 다음과 같다.
const obj = {
then(onFulfilled) {
console.log('inside then method');
console.log(onFulfilled.toString());
onFulfilled('resovled');
},
};
console.log('start');
const result = await obj;
console.log('result: ', result);
console.log('end');
// output>
// start
// inside then method
// function () { [native code] }
// result: resolved
// end
const obj = {
then(onFulfilled, rejected) {
console.log('inside then method');
let i = Math.floor(Math.random() * 10);
if (i % 2 == 0) {
onFulfilled('resovled');
} else {
rejected('rejected');
}
},
};
try {
console.log('start');
const result = await obj;
console.log('result: ', result);
} catch (error) {
console.log('error', error);
} finally {
console.log('end');
}
두 번째 인자를 전달하여 호출하게 되면 연산의 실패를 알릴 수 있게 된다. 이를 통해 await
문에서 try…catch
문을 통해 에러 핸들링도 가능하게 된다.
thenable객체를 활용한다면 비동기 처리 과정에서 특정 로직을 체이닝이나 await를 통해 함께 처리할 수 있게 된다. 다음은 간단한 예이다.
const asyncFunction = (n) =>
new Promise((resolve, reject) => {
if (n % 2 > 0) resolve('it is resolved');
else reject('it is rejected');
});
const mathOp = (value) => {
const op = {
add(n) {
value += n;
return op;
},
sub(n) {
value -= n;
return op;
},
mul(n) {
value *= n;
return op;
},
div(n) {
value /= n;
return op;
},
then(callback, rejected) {
return asyncFunction(value).then(callback, rejected);
},
};
return op;
};
try {
const result = await mathOp(5).add(2).sub(3).mul(-1).add(5).add(1);
console.log(result);
} catch (error) {
console.log('error', error);
}
위 코드는 홀수인지 짝수인지에 따라 성공 실패 결과를 알려주는 로직이다. 체이닝을 통해 연산을 진행하고 마지막 판단 로직을 then()메소드를 통해 만들어 await
로 외부에 값을 전달할 수 있게 되었다.
이처럼 thenable객체는 체이닝이나 await
키워드를 통해 가독성 좋은 코드를 만들 수 있는 이점이 있다.
thenable 객체는 promise만의 것이 아니다. thenable객체를 직접 만들어서 사용한다면 비동기 과정에서 일어나는 일련의 과정 사이에 원하는 로직을 추가하여 더 풍부하고 가독성 좋은 코드를 만들 수 있게 된다.