프로미스는 비동기 작업을 조금 더 편하게 처리할 수 있도록 ES6에 도입된 기능이다.
이전에는 비동기 작업을 처리할 때 콜백함수로 처리했어야 했는데, 비동기 작업이 많아질 경우 코드가 쉽게 난잡해지게 되었다.
예) 숫자 n을 파라미터로 받아와서 다섯번에 걸쳐 1초마다 1씩 더해서 출력하는 작업을
setTimeout
으로 구현
function increaseAndPrint(n, callback) {
setTimeout(() => {
const increased = n + 1;
console.log(increased);
if (callback) {
callback(increased);
}
}, 1000);
}
increaseAndPrint(0, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
console.log('끝!');
});
});
});
});
});
이런 식으로 코드가 많아지면서 읽기 복잡해진다. 이런 식의 코드를 콜백지옥(Callback Hell)이라고 부른다..
비동기적으로 처리해야하는 일이 많아질수록, 코드의 깊이가 계속 깊어지는 현상이 있다.
하지만 Promise를 사용하면 이렇게 코드의 깊이가 깊어지는 현상을 방지할 수 있다.
const myPromise = new Promise((resolve, reject) => {
// 구현..
})
Promise를 만들때에는 resolve
와 reject
를 파라미터로 받아오는 함수를 인자로 넣어주어야 한다.
성공할 때에는 resolve
를 호출해주고, 실패할 때에는 reject
를 호출해주면 된다.
다만, reject
는 사용하지 않는다면 생략할 수 있다.
1초 뒤에 성공시키는 상황에 대해서 구현하였다.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
myPromise.then(n => {
console.log(n);
});
resolve
를 호출 할 때 특정값을 파라미터로 넣어주면, 이 값을 작업이 끝나고나서 사용할 수 있다.
Promise 작업이 끝나고 나서 또 다른 작업을 해야할 때에는 Promise 뒤에 .then(...)
을 붙여서 사용하면 된다.
이번에는 1초 뒤에 실패되게끔 구현해보자.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error());
}, 1000) // 1초뒤에 에러를 발생시킴
});
myPromise.then(result => {
console.log(result);
}).catch(e => {
console.error(e);
})
Promise의 catch
함수는 앞서 만든 Promise에서 reject
한 값을 받아올 때 사용한다.
여기서 e
는 reject
에 인자로 넣어준 값이다.
실패하는 상황에서는 reject
를 사용하고, .catch
를 통해서 실패했을 때 실행될 작업을 설정할 수 있다.
console.log
와 달리 console.error
는 빨간색으로 나와, 콘솔에서 에러가난 것을 더욱 쉽게 확인할 수 있게 해준다.
Promise 함수를 만들어보자.
function increaseAndPrint(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if (value === 5) {
const error = new Error(); // value가 5가 됐을 때 에러를 발생시킴 (실패 상황)
error.name = 'ValueIsFiveError'; // 에러이름을 설정함
reject(error);
return; // 작업 종료
}
console.log(value); // 실패하는 상황이 아닌 경우
resolve(value);
}, 1000) // 1초 뒤에 함수가 실행됨
})
}
increaseAndPrint(0).then(n => {
console.log('result: ', n);
})
// >> 1
// >> result: 1
increaseAndPrint
함수를 실행시켰을 때
1초 뒤에
파라미터에 받아온 n 값에 1을 더해주고
console.log(value)
를 통해 value
값을 출력해준 뒤,
resolve
를 통해서 1을 더해준 값을 반환해준다. >>> 1
Promise 작업이 끝난 뒤 .then
이 실행된다. >>> result: 1
여기까지만 보면, 결국 함수를 전달하는 것인데 차이점을 못느낄 수 있다.
Promise 속성 중에는 .then
내부에 넣은 함수에서 또 Promise를 리턴하게 된다면, 연달아서 사용할 수 있다.
function increaseAndPrint(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if (value === 5) {
const error = new Error();
error.name = 'ValueIsFiveError';
reject(error);
return;
}
console.log(value);
resolve(value);
}, 1000);
});
}
increaseAndPrint(0)
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.catch(e => {
console.error(e);
});
위 코드를 아래와 같이 정리할 수 있다.
function increaseAndPrint(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if (value === 5) {
const error = new Error();
error.name = 'ValueIsFiveError';
reject(error);
return;
}
console.log(value);
resolve(value);
}, 1000);
});
}
increaseAndPrint(0)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.catch(e => {
console.error(e);
});
Promise를 사용하면, 비동기 작업의 개수가 많아져도 코드의 개수가 깊어지지 않게된다.
🚨 단점
async/await
을 사용해서 해결할 수 있다!