자바스크립트와 Node는 비동기 프로그래밍으로 이벤트 주도 방식을 활용하면서 콜백 함수를 많이 사용하게 된다. 콜백 함수 자체가 복잡한 것도 있고, 이해하기 어려운 자바스크립트 내용 중 하나이기도 하다.
이에 ES2015부터는 콜백 대신 API들이 프로미스 기반으로 재구성되고 있다. 따라서 프로미스에 대해 잘 이해하고 사용하게 된다면, 복잡한 콜백 함수의 지옥에서 벗어날 수 있으니 확실히 알고 있어야 한다.
Promise
객체 구조는 다음과 같다.
const condition = true;
const promise = new Promise((resolve, reject) => {
if (condition){
resolve('성공');
} else {
reject('실패');
}
});
promise
.then((message) => {
console.log(message);
})
.catch((error) => {
console.log(error);
});
new Promise
로 Promise
를 생성할 수 있다. 그리고 안에 resolve
와 reject
를 매개변수로 갖는 콜백 함수를 넣는 방식이다.
이제 선언한 Promise
변수에 then
과 catch
메소드를 붙일 수 있다.
resolve
가 호출되면then
이 실행되고,reject
가 호출되면catch
가 실행된다.
이제 resolve
와reject
에 넣어준 인자는 각각 then
과 catch
의 매개변수에서 받을 수 있다.
즉,
condition === true
라면, resolve('성공')
이 호출되어 message
에 '성공'
이 들어가 콘솔에 출력된다.condition === false
라면, reject('실패')
가 호출되어 catch
문이 실행되고 error
에 '실패'
가 들어가 콘솔에 출력된다.이러한 방식을 활용해서 콜백을 프로미스로 바꿔보자.
function findAndSaveUser(Users) {
Users.findOne({}, (err, user) => { // 첫번째 콜백
if(err) {
return console.error(err);
}
user.name = 'kim';
user.save((err) => { // 두번째 콜백
if(err) {
return console.error(err);
}
Users.findOne({gender: 'm'}, (err, user) => { // 세번째 콜백
// 생략
});
});
});
}
보통 콜백 함수를 사용하는 패턴은 이와 같이 작성한다. 현재는 콜백 함수가 3번 중첩된 모습을 볼 수 있다.
즉, 콜백 함수가 나올 때 마다 코드가 길어지고 깊어지며, 각 콜백 함수마다 에러도 따로 처리해줘야 한다.
하지만, Promise
를 활용하면 다음과 같이 작성할 수 있다.
function findAndSaveUser1(Users) {
Users.findOne({})
.then((user) => {
user.name = 'kim';
return user.save();
})
.then((user) => {
return Users.findOne({gender: 'm'});
})
.then((user) => {
// 생략
})
.catch(err => {
console.error(err);
});
}
then
을 활용해 코드가 깊어지지 않도록 만들었다. 이때 then
메소드들은 순차적으로 실행된다.
에러는 마지막 catch
를 통해 한번에 처리할 수 있다. 하지만 모든 콜백 함수를 이처럼 고칠 수 있는 것은 아니고, find
와 save
메소드가 Promise
방식을 지원하기 때문에 가능하다.
Promise
방식을 지원하지 않는 콜백 함수는util.promisify
를 통해 가능하다.
Promise 여러 개를 한번에 실행할 수 있는 방법도 존재하는데, Promise.all
을 사용한다.
const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
Promise.all([promise1, promise2])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(err);
});
promise.all
에 해당하는 모든 Promise
가 resolve 상태
여야 then
으로 넘어간다.
만약, 하나라도 reject
가 있다면 catch 문
으로 넘어간다.
기존의 콜백을 활용한다면, 여러 번 중첩해서 구현해야 하지만 Promise
를 사용하면 깔끔하게 코드 작성이 가능하다.
Promise 패턴
을 사용하면 비동기 작업들을 순차적으로 진행하거나, 병렬로 진행하는 등 컨트롤이 보다 수월해진다. 또한, 내부적으로 예외처리에 대한 구조가 존재하기 때문에 오류 처리 등에 대해 보다 가시적으로 관리할 수 있다.
Promise 패턴은 ECMAScript6 스펙에 정식으로 포함되었다.