[자바스크립트/Javascript] Promise
Promise
프로미스는 비동기 작업을 조금 더 편하게 처리할 수 있도록 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를 사용하면 이렇게 코드의 깊이가 깊어지는 현상을 방지할 수 있다.
Promise 사용법
const myPromise = new Promise((resolve, reject) => {
// 구현..
})
Promise를 만들때에는 resolve와 reject를 파라미터로 받아오는 함수를 인자로 넣어주어야 한다.
성공할 때에는 resolve
를 호출해주고, 실패할 때에는 reject
를 호출해주면 된다.
다만, reject는 사용하지 않는다면 생략될 수 있다.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
myPromise.then(n => {
console.log(n);
});
1초 뒤에 성공시키는 상황에 대해서 구현하였다.
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
이번에는 콜백을 통해서 그 다음 처리할 작업을 넣어주지 않고 Promise 의 then 을 통해서 하기 위해 파라미터에서 콜백을 없앴다.
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);
});
위 코드를 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(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.catch(e => {
console.error(e);
});
Promise를 사용하면, 비동기 작업의 개수가 많아져도 코드의 개수가 깊어지지 않게 된다.
🚨 단점
- 에러를 잡을 때 몇 번째에서 발생했는지 알아내기 어렵다.
- 특정 조건에 따라 분기를 나누는 작업이 어렵다.
- 특정 값을 공유해가면서 작업을 처리하기 까다롭다.
이 문제점들은 async/await 을 사용해서 해결할 수 있다! 다음 내용도 확인해보자.