콜백함수와 프로미스의 차이
function add10(a, callback) {
setTimeout(() => callback(a+10), 100);
}
add10(5, res=> {
console.log(res);
}) // 15
function add20(a) {
return new Promise(resolve => setTimeout(()=>resolve(a+20), 100))
}
add20(5).then(console.log); // 25
add20(5).then(add20).then(console.log); // 45
프로미스의 가장 중요한 특징은 콜백함수와 달리 결과를 값으로 받아 저장할 수 있다는 것이다. 프로미스를 받아 then으로 이어 또 다른 작업을 할 수 있기 때문에, 코드도 간결해질 뿐더러 비동기 프로그래밍을 안정적으로 수행한다.
값으로서의 프로미스 활용
- 어떤 값을 함수에 넣어서 리턴하는 함수가 있다고 할 때, 만약 그 값이 비동기적으로 전달되면 어떨까?
const go1 = (a,f) => f(a);
const add5 = a => a + 5;
console.log(go1(10, add5)); // 15
const delay100 = a => new Promise(resolve => setTimeout(()=>resolve(a), 100));
console.log(go1(delay100(10), add5)); // [object Promise]5
- delay100이라는 함수로 비동기적으로 값을 전달하면 제대로된 값을 출력하지 못한다.
const go1 = (a,f) => a instanceof Promise ? a.then(f) : f(a);
const add5 = a => a + 5;
const delay100 = a => new Promise(resolve => setTimeout(()=>resolve(a), 100));
console.log(go1(delay100(10), add5));
// Promise {<pending>} 펼쳐보면
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: 15
// undefined
const result = go1(delay100(10), add5);
result.then(console.log)
// Promise {<pending>}
// 15
- 이때 go1이라는 함수를 변경해서, 만약 그 값이 프로미스의 인스턴스인지 체크한 뒤 비동기 프로그래밍 해주면 동기적으로 값이 전달될 때나 비동기적으로 값이 전달될 때나 관계없이 똑같은 값을 출력할 수 있다.
함수 합성
// 합성 함수
const g = a => a + 1;
const f = a => a * a;
console.log(f(g(1))); // 4
console.log(f(g())); // NaN
// 값이 없어도 강제 합성이 되고 결과를 리턴
[1].map(g).map(f).forEach(r => console.log(r)); // 4
[].map(g).map(f).forEach(r => console.log(r)); // log nth
Promise.resolve(1).then(g).then(f).then(r=>console.log(r))
- 어레이는 맵을 통해서 안전하게 함수를 합성 -> 유효하지 못한 효과가 리턴되는 것을 방지(모나드)
- 프로미스는 then을 통해 안전하게 함수를 합성 -> 비동기적 상황을 효과적으로 다룸(그러나 값이 전달되지 않는 경우 NaN을 리턴함)
Kleisli Composition
- Kleisli Composition: 오류가 있을 수 있는 상황에서 안전하게 함수를 합성
- 스테이트가 변경되어 함수 합성에 오류가 있는 경우
var users = [
{ id: 1, name: 'aa'},
{ id: 2, name: 'bb'},
{ id: 3, name: 'cc'},
]
const getUserById = id => users.find(u => u.id === id);
const f = ({name}) => name;
const g = getUserById;
const fg = id => f(g(id));
users.pop();
users.pop();
fg(2); // error!
g(2); // undefined
// 오류가 있을 수 있는 상황에서 fg와 g는 다른 결과를 출력
const fg = id => Promise.resolve(id).then(g).then(f);
const getUserById = id => users.find(u => u.id === id) || Promise.reject('없어요');
- 프로미스를 사용하여 합성하면 g(2), fg(2)는 동일하게
Promise {<rejected>: "없어요"}
를 출력한다.