프로미스 객체란?
프로미스(Promise) 객체는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다.
즉 비동기 함수의 실행 상태에 따라 각각의 함수를 호출하여 작업의 결과를 나타내는 것입니다.
예시
비동기적으로 이미지파일을 생성하는 createImageFile() 이라는 함수가 있을 때, 이 함수의 파라미터로는 (1)이미지 파일의 이름, (2)성공 시 호출할 함수, (3)실패 시 호출할 함수가 있습니다.
function createImageFile(fileName, successCallBack, failureCallBack){
// 이미지 파일 생성중
if(success) successCallBack(fileName);
else failureCallBack(fileName);
}
프로미스가 나타낼 수 있는 상태는 세 가지가 있습니다.
- 대기(pending) : 이행하지도, 거부하지도 않은 초기 상태
- 이행(fulfilled) : 작업이 완료된 상태(성공)
- 거부(rejected) : 작업이 거부된 상태(실패)
대기 상태에서 작업을 성공적으로 완료하는 것을 해결(resolve)라고 하고, 대기 상태에서 작업이 오류 발생 등 모종의 이유로 실패하는 것을 거부(reject)라고 합니다.
프로미스 객체 사용해보기
프로미스 객체는 함수를 콜백을 전달하는 대신에 콜백을 첨부하는 방식입니다.
위에서 살펴본 프로미스 객체의 자세한 사용 방법을 알아보겠습니다.
객체 생성
let myPromise1 = new Promise((resolve, reject) => {
// 비동기 작업
});
프로미스 객체의 생성자를 사용했습니다.
프로미스의 생성자 매개변수에는 콜백 함수가 들어가고, 그 콜백함수는 성공과 실패를 각각 나타내는 두 개의 객체를 매개변수로 받습니다.
resolve는 비동기 처리가 성공했을 때 호출하는 함수, reject는 비동기 처리가 실패했을 때 호출하는 함수입니다.
객체 처리 (then, catch, finally)
then
then은 프로미스에서 가장 중요한 메서드입니다.
사용 방법은 아래와 같습니다.
myPromise.then(
function(){}, // 성공 시 싫행하는 함수
function(){} // 실패 시 실행하는 함수
);
.then()의 첫 번째 인수는 프로미스의 비동기 처리가 성공했을 때 실행하는 함수이고,
두 번째 함수는 프로미스의 비동기 처리가 실패했을 때 실행하는 함수입니다.
catch
만약 프로미스의 비동기 처리가 실패한 경우만 다루고 싶다면 .catch()를 사용하면 됩니다.
myPromise.catch(
function(){} // 실패시 실행할 함수
);
.catch()를 사용하는 것은 .then()의 첫 번째 인수에 null을 입력하는 것과 같습니다.
*프로미스는 성공 또는 실패만 합니다.
하나의 비동기 처리에 대해 프로미스는 성공 또는 실패만 할 수 있습니다.
따라 resolve를 실행하면 reject를 호출할 수 없고, reject를 실행하면 resolve를 실행할 수 없습니다.
finally
그럼 성공과 실패와 상관 없이 무조건 실행되게 하는 함수는 어떻게 처리해야 할까요?
finally를 사용하여 처리할 수 있습니다.
promise
.then(value => {
// 성공
console.log(`success data : ${value}`);
})
.catch(() => {
// 실패
console.error('failure');
})
.finally(() => {
console.log('end');
})
함수 등록
위에서 본 방법들은 모두 객체에 바로 함수를 부착하는 방식입니다.
이번에는 프로미스 객체를 반환하는 함수를 만들어 함수의 return 값을 사용해보겠습니다.
function getPromise(){
return new Promise((resolve, reject) => {
// 작업
if(isResolved) resolve();
else reject();
});
}
getPromise()
.then(() => { console.log("작업 성공") })
.catch(() => { console.log("작업 실패") });
함수 등록 방식을 이용하는 이유는 다음과 같습니다.
- 반복되는 비동기 작업을 효율적으로 처리할 수 있습니다.
- 코드 구조가 명확해져 비동기 작업의 정의와 사용을 분리하여 가독성을 높일 수 있습니다.
- 인자를 전달하여 동적으로 비동기 작업을 수행할 수 있습니다.
위 함수는 프로미스 팩토리함수라고도 합니다.
Promise Chaining
프로미스의 가장 뛰어난 장점 중 하나인 Chaining에 대해 알아보겠습니다.
promise chaining은 이전 단계의 비동기 작업이 성공하고 나서의 결과값을 가지고 다음 비동기 작업을 수행해야 할 때 유용하게 쓰입니다.
체이닝을 사용하지 않았을 때
// 함수 정의
const getDataCallback = (num, callback) => {
setTimeout(()=>{
if(typeof num === 'number')
callback(undefined, num * 2);
else
callback('숫자가 아닙니다.', num);
},1000);
};
// 함수 호출
getDataCallback(2, (errMsg, data) => {
if(errMsg !== undefined){
console.log(errMsg)
}
else{
getDataCallback(data, (errMsg, data) => {
if(errMsg !== undefined) {
console.log(errMsg);
}else{
getDataCallback(data, (errMsg, data) => {
if(errMsg !== undefined) {
console.log(errMsg);
}else{
console.log(data);
}
});
}
});
}
});
프로미스를 사용하지 않고, 비동기 작업을 연속적으로 수행하게 되면
콜백 지옥이 생겨납니다.
체이닝을 사용할 때
const getDataPromise = num => new Promise((resolve, reject) =>{
setTimeout(() => {
typeof num === 'number' ? resolve(num * 2) : reject('숫자가 아닙니다.');
}, 1000);
});
getDataPromise(2)
.then(data => getDataPromise(data))
.then(data => getDataPromise(data))
.then(data => console.log(data)) // 16을 출력
.catch(errMsg => console.log(errMsg));
하지만 프로미스를 이용하면 코드가 더욱 간결해 보입니다.
이 때 주의해야 할 사항은 반환값이 반드시 있어야 한다는 것입니다.
반환값이 없다면 당연히 이전의 콜백 함수가 이전의 결과를 받지 못합니다.
promise chaining을 사용하는 이유
- 코드를 더 효율적으로 짜기 위해 사용된다
- 비동기 코드를 간단하게 짤 수 있다
- 여러개의 프로미스 체인에서 하나라도 reject되면 바로 catch로 내려가서 error를 처리한다
* 위 글은 다음 문서를 참고하여 작성되었습니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
'Programming Language > JavaScript' 카테고리의 다른 글
[JavaScript] Window.lacalStorage와 Window.sessionStorage (0) | 2023.10.01 |
---|---|
[JavaScript] async와 await (0) | 2023.07.29 |
[JavaScript] 비동기 처리와 콜백 함수 (0) | 2023.07.19 |
[JavaScript] 데이터 단위 변환기 토이 프로젝트 만들어보기 (0) | 2023.05.06 |
[JavaScript] DOM 객체로 HTML 요소의 속성 바꾸기 (0) | 2023.05.05 |