기존 콜백 방식은 중첩이 깊어질수록 읽기 어렵다.
add10(5, res => {
add10(res, res => {
add10(res, res => {
log(res);
});
});
});
Promise의 .then 체이닝은 이걸 평탄하게 만들지만, 함수형 파이프라인(go, pipe)과 섞어 쓰기 위해서는 추가 작업이 필요하다.
Promise도 변수에 담거나 인자로 넘기거나 반환할 수 있다. 이 점을 활용해서 go1을 만들었다.
const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
인자가 Promise면 .then으로 연결하고, 아니면 즉시 실행한다.
go는 내부적으로 reduce를 쓰는데, reduce가 Promise를 받으면 .then으로 연결하도록 수정했다.
const reduce = curry((f, acc, iter) => {
// ...
return go1(acc, function recur(acc) {
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
acc = f(acc, a);
if (acc instanceof Promise) return acc.then(recur);
}
return acc;
});
});
이제 go 안에서 Promise를 반환하는 함수가 있어도 자연스럽게 동작한다.
go(
Promise.resolve(1),
a => a + 10,
a => Promise.resolve(a + 100),
log
); // 111
중간에 Promise.reject가 발생하면 이후 함수들을 건너뛰고 .catch로 전달된다.
go(
Promise.resolve(1),
a => a + 10,
a => Promise.reject('error'),
a => a + 1000, // 실행 안 됨
log
).catch(e => log(e)); // 'error'
함수형 파이프라인 안에서 비동기 에러 처리도 선언적으로 된다.