파트9. 비동기 동시성 프로그래밍(1) - ChoDragon9/posts GitHub Wiki
Callback과 Promise의 차이
Promise는 비동기를 값으로 다룰 수 있다는 것이 큰 차이이다. 그래서 콜백으로 처리하는 함수는 리턴되는 값이 없지만 Promise로 처리하는 함수는 리턴된 Promise를 통해서 연속적인 동작을 할 수 있다.
const add10 = (a, callback) => {
setTimeout(() => callback(a + 10), 100);
};
add10(10, res => {
add10(res, res => {
console.log(res);
});
});
const add20 = (a, callback) => {
return new Promise(resolve => setTimeout(() => resolve(a + 20), 100));
};
add20(10)
.then(add20)
.then(console.log);
합성 관점에서의 Promise와 모나드
모나드는 함수합성을 안전하게 하기 위한 컨테이너입니다. 비정상적인 값이 인자로 들어올 때 비정상적인 효과를 만들지 않기 위해 사용합니다.
// (f . g)(x) = f(g(x))
const g = a => a + 1;
const f = a => a * a;
f(g(1)); // 정상적인값
f(g()); // 비정상적인값
[1].map(g).map(f);
[].map(g).map(f);
Promise는 비동기 상황일 때 안전한 합성을 하게 합니다. Array와 비슷한 방법으로 기술이 가능합니다.
Array.of(1).map(g).map(f).forEach(r => console.log(r));
Promise.resolve(2).then(g).then(f).then(r => console.log(r))
Promise는 비동기 상황의 안전한 합성을 하게되는 데, 합성하는 시점을 안전하게 하는 용도의 안전한 합성을 하게 됩니다. 데이터의 문제가 있을 때 상황에는 용도가 맞지 않고 비정상적인 값이 왔을 때는 비정상적으로 동작됩니다.
Promise.resolve()
.then(g)
.then(f)
.then(r => console.log(r)) // NaN
Kleisli Composition
함수 합성시 오류가 발생하는 상황일 때 안전한 합성을 처리하는 방법중 하나. 함수나 인자에 문제가 발생했을 때 f(g(x)) = g(x)
가 성립되도록 구현하는 것입니다.
// f . g
// f(g(x)) = f(g(x))
// f(g(x)) = g(x)
const users = [
{id: 1, name: 'aa'},
{id: 2, name: 'bb'},
{id: 3, name: 'cc'}
];
const getUserById = id =>
users.find(u => u.id == id) || Promise.reject('없어요!');
const f = ({name}) => name;
const g = getUserById;
const fg = id => Promise.resolve(id).then(g).then(f);
fg(3).then(console.log);
g(3);
비동기 제어
reduce에 Promise를 사용가능하도록 하면 안정적인 함수 합성을 할 수 있습니다.
const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
const reduce = curry((f, acc, iter) => {
if(!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
} else {
iter = iter[Symbol.iterator]();
}
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.resolve(1),
a => a + 10,
a => Promise.resolve(a + 100),
a => a + 1000,
console.log
)
go(Promise.resolve(1),
a => a + 10,
a => Promise.reject('error'),
a => console.log('---'),
a => a + 1000,
console.log
).catch(a => console.log(a))
promise.then의 중요한 규칙
Promise.resolve(Promise.resolve(Promise.resolve(1))).then(console.log);