우리는 가끔 실수로 javascript로 코드를 짜면서 비동기 처리를 할 때 async를 걸고 await을 안걸었을 때 결과의 객체가 promise로 나와서 에러가 발생한 경우를 격어 보았을 수도 있을 것이다. 필자의 promise에 대한 첫 인상은 그랬다. 객체이긴 객체인데 promise? 약속? 이 뭘까? 이 부분에 대해서 알아보자
Promise는 JavaScript에서 비동기 처리를 다룰 때 사용하는 객체이다.
Promise를 사용하면 비동기 처리 결과를 쉽게 처리할 수 있다고 한다.
그러다면 promise는 왜 생겨났을까? javascript는 원래 동기적인 처리를 기반으로 한 줄씩 코드를 읽어나가면서 동작을 한다. 동기적 동작을 하는 javascript에서 비동기적으로 코드를 동작시켜야 할 경우가 발생할 때는 콜백 함수를 사용해 처리하였다.
콜백 함수를 사용하면 비동기 처리 결과를 처리할 수 있었지만, 콜백 함수를 중첩하여 사용하면 코드의 가독성이 나빠지고, 디버깅이 어렵고, 에러 핸들링이 복잡해졌다고 한다.
getData1(function(result1) {
getData2(result1, function(result2) {
getData3(result2, function(result3) {
getData4(result3, function(result4) {
// ...
});
});
});
});
Promise는 위와같은 이러한 콜백 지옥을 해결하고, 비동기 처리 코드의 가독성과 유지 보수성을 높이기 위해 ES5부터 도입되었다.
Promise는 세 가지 상태를 가지며, 상태에 따라 처리 결과를 다룬다.
- Pending: 아직 처리되지 않은 상태
- Fulfilled: 처리가 성공적으로 완료된 상태
- Rejected: 처리가 실패한 상태
Promise를 생성하면, 해당 Promise는 Pending 상태가 된다.
Promise가 성공적으로 처리되면, 해당 Promise는 Fulfilled 상태가 되고, 처리가 실패하면 Rejected 상태가 된다.
Promise는 then() 메서드와 catch() 메서드를 가지며, then() 메서드는 Fulfilled 상태일 때, catch() 메서드는 Rejected 상태일 때 호출된다. then() 메서드와 catch() 메서드는 Promise 객체를 반환하며, 이를 체이닝하여 처리 결과를 다룰 수 있다.
Promise 객체를 생성할 때, 처리할 작업을 인자로 전달한다. 처리 결과가 성공적으로 완료되면, resolve() 함수를 호출하고, 처리가 실패하면 reject() 함수를 호출한다.
아래의 예시를 들어보자
function getData() {
return new Promise((resolve, reject) => {
// 비동기 작업
setTimeout(() => {
const data = [1, 2, 3, 4, 5];
if (data.length > 0) {
resolve(data);
} else {
reject(new Error('No Data'));
}
}, 1000);
});
}
getData()
.then((data) => console.log(data))
.catch((err) => console.error(err));
getData() 함수가 Promise 객체를 반환한다. Promise 객체는 비동기 처리 작업을 수행하며, 처리 결과에 따라 Fulfilled 상태 또는 Rejected 상태가 된다.
promise의 도입배경과 사용방법에 대해 알아봤으니 이것을 사용하면 어떤것이 좋고 나쁜지 알아보자
참고자료(출처)
동기, 비동기란? (+Promise, async/await 개념)
JAVASCRIPT PROMISES – THE DEFINITIVE GUIDE
JavaScript Promise란?