다음은 지옥의 콜백 코드 예시이다.
예시를 보고 콜백체인의 문제점이 무엇인지 알아보자.
class UserStorage {
loginUser(id, password, onSuccess, onError) {
setTimeout(() => {
if (
(id === 'seungha' && password === '1234') ||
(id === 'ha' && password === '0987')
) {
// 로그인 성공 시 콜백하는 함수
onSuccess(id);
} else {
// 로그인 실패 시 콜백
onError(new Error('not found'));
}
}, 2000);
}
// 로그인이 성공했을떄
// 유저의 역할을 얻어오는 콜백 함수
getRoles(user, onSuccess, onError) {
setTimeout(() => {
if (user === 'seungha') {
// 성공 시 콜백
onSuccess({ name: 'seungha', role: 'admin' });
} else {
// 실패 시 콜백
onError(new Error('no access'));
}
}, 1000);
}
}
const userStorage= new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your password');
// loginUser함수 실행
userStorage.loginUser(
id,
password,
(id) => {
// onSuccess()
// 로그인 성공 시 getRoles()콜백 실행
userStorage.getRoles(
id,
// onSuccess()
// 역할이 있는 경우 실행
(userWithRole) => {
alert(
`hello ${userWithRole.name}, you have a ${userWithRole.role} role`);
},
// onError() 역할을 알 수 없는 경우 실행
(error) => {
console.log(error)
}
);
},
// onError() 로그인 실패 시 실행
(error) => {
console.log(error);
}
);
가독성 문제
함수가 어느 위치에서 어떤 식으로 연결되어 있는지, 가늠하기 어렵다.
전체적인 비즈니스 로직을 한눈에 알아보기 어렵기 때문에 유지보수나 디버깅에도 문제가 있을 수 있다.
위와 같은 비동기적인 문제를 해결하기 위해 promise
라는 개념을 사용할 수 있다.
promise
는 자바스크립트 안에 내장되어있는 오브젝트로 콜백 함수 대신 비동기적으로 작업을 수행할 때 사용할 수 있다.
promise
을 다음의 두가지를 이해해야한다.
Promise
는 클래스이므로 new
키워드를 사용하여 생성 할 수 있다.
Promise
의 생성자는 콜백 함수를 가지고 있으며 이 콜백 함수가 가지는 생성자는 다음과 같은 인자를 가진다.
(resolve (기능을 정상적으로 수행), reject (기능에 문제가 생겼을 때 수행))
const promise = new Promise((resolve, reject) => {
console.log('doing something...');
setTimeout(() => {
resolve('seungha');
// reject(new Error('no network'));
}, 2000);
});
Promise
의 콜백 함수는 new
키워드로 Promise
를 만드는 순간 바로 실행이 된다.
생성한 Promise
를 사용해보자.
Promise
의 상태에 따라 기능을 체이닝하여 정의할 수 있다.
promise
.then(value => { // resolve (성공)
console.log(value);
})
.catch(error => { // reject (실패)
console.log(error);
})
.finally(() => { // 성공 실패 여부와 상관없이 수행
console.log('finally');
});
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
});
fetchNumber
.then(num => num * 2)
.then(num => num * 3)
.then(num => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(num - 1), 1000);
});
})
.then(num => console.log(num));
then
을 묶어서 비동기적인 것들을 묶어서 처리 할 수 있다.
여러 Promise
가 함께 사용되는 경우에 오류처리를 어떻게 해야할까?
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = hen =>
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000);
});
const cook = egg =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen() //
// hen => getEgg(hen) == getEgg
// 받아온 값을 인자로 사용할 때에는 축약하여 사용가능
.then(getEgg)
// getEgg함수에서 오류가 발생하더라도 전체적힌 로직의 흐름에는 문제가 생기지 않도록 대비
.catch(error => {
return '🍗';
}
.then(cook)
.then(console.log)
// error를 가장 마지막에서 처리
.catch(console.log);