Promise객체는 저번 포스팅의 마지막에 나와있던 콜백지옥으로 인한 가독성 또는 에러를 보완해주는 객체이다.
Promise객체를 알아보기전에 먼저 비동기 작업이 가질 수 있는 3가지 상태가있다.
비동기 작업이 가질 수 있는 3가지 상태
Pending(대기 상태)
대기상태로 현재 비동기작업이 진행중이거나 작업이 시작할 수도없는 뭔가 문제가 발생했음을 알림
Fulfilled(성공)
이행 또는 성공 상태로 비동기작업이 의도대로 정상 작동이됐다는걸 알림
Rejected(실패)
거부 또는 실패 상태로 비동기작업이 모종의이유로 실패했음을 알림
일단 이 세가지 상태를 갖는다 라는것만 이해해주면 됨.
비동기 작업은 한번 성공하거나 실패하면 그걸로 작업이 끝난다고 생각하면 됨.
그래서 비동기 작업이 펜딩상태(대기 상태)에서 성공 으로 갈때 resolve 실패로 갈때 reject가 일어나는걸 알아둬야한다.
resolve와 reject가 일어나는 상황을 보기위해 코드를 실행을 해보면
function isPositive(number,resolve,reject){
setTimeout(()=>{
if(typeof number === 'number'){
// 성공 -> resolve
resolve(number >=0 ? '양수' : '음수')
}else{
// 실패 -> reject
reject('주어진 값이 숫자형 값이 아님')
}
},2000)
}
isPositive(10,(res)=>{
console.log('성공적으로 수행됨 : ',res)
},(err)=>{
console.log('실패 했음 : ',err)
});
위 값을 실행시켜보면 number 타입에 맞게 숫자를 넣어 입력을 하면 성공적으로 수행이되고
빈 배열을 넣으면 실패하는 값을 볼 수 있다.
위는 10을 넣었을때 양수, 배열을 넣었을때 실패 숫자형이 아니라 알려줌.
근데 이 결과값을 받는곳을 Promise를 이용해서 만들어보면 위 함수는 남겨두고 호출부만 변경하면된다.
function isPositiveP(number){
const executor = (resolve,reject)=>{ // 실행자
setTimeout(()=>{
if(typeof number === 'number'){
// 성공 -> resolve
resolve(number >=0 ? '양수' : '음수')
}else{
// 실패 -> reject
reject('주어진 값이 숫자형 값이 아님')
}
},2000)
};
const asyncTask = new Promise(executor);
return asyncTask;
}
const res = isPositiveP();
res.then((res)=>{
console.log('작업성공 : ',res)
}).catch((err)=>{
console.log('작업실패 :',err)
})
위 처럼 isPositiveP 라는 새 함수를 생성한 뒤. number라는 파라미터를 뚫어준 뒤
executor라는 실행자를 만들고 그 안에 비동기로 처리할 작업을 넣는다.
비동기로 처리할 작업은 위에 설명해둔것 처럼 resolve,reject로 조건문으로 값을 받게끔 넘긴다
그리고 asyncTask 라는 변수에 new Promise(executor); 를 넣어 executor를 실행해줄 변수를 만들고
밖에서 isPositiveP()를 실행시킨다.
res라는 변수에 담아서 실행시키는데 res.then(()=>{코드}).catch(()=>{코드})
위 처럼 ajax사용했던것처럼 성공했을때 실행시킬 코드 실패했을때 실행할 코드를 따로 적어두면 끝
promise객체는 이런식으로 사용이되고 저번시간의 콜백지옥을 다시 재현해보면
function taskA(a,b,cb){
setTimeout(()=>{
const res = a + b;
cb(res);
},3000);
}
function taskB(a,cb){
setTimeout(()=>{
const res = a * 2;
cb(res);
},1000)
}
function taskC(a,cb){
setTimeout(()=>{
const res = a * -1;
cb(res);
},2000)
}
taskA(3,4,(a_res)=>{
console.log("taskA : ",a_res);
taskB(a_res,(b_res)=>{
console.log("taskB : ",b_res);
taskC(b_res,(c_res)=>{
console.log("taskC : ",c_res);
})
})
})
이 처럼 콜백이 안쪽으로 계속 길어지는걸 콜백지옥이라 한다.
이 콜백지옥을 해결하기위한 Promies를 사용해보자.
function taskA(a,b){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const res = a + b;
resolve(res);
},3000);
})
}
function taskB(a){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const res = a * 2;
resolve(res);
},1000)
})
}
function taskC(a){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const res = a * -1;
resolve(res);
},2000)
})
}
taskA(5,1).then((a_res)=>{
console.log('A result : ',a_res);
taskB(a_res).then((b_res)=>{
console.log('B result : ',b_res);
taskC(b_res).then((c_res)=>{
console.log('C result :',c_res)
})
})
})
위 처럼 Promise를 사용해서 만들어볼 수 있는데 Promise객체를 사용하면 콜백지옥을 없앨 수 있는 장점이 있다했는데 위의코드는 기대하던것과는 달리 똑같이 콜백지옥이 발생하였다.
Promise의 객체와 메서드인 then을 저런식으로 사용하는게 아니라서 그럼 수정된 코드는
function taskA(a,b){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const res = a + b;
resolve(res);
},3000);
})
}
function taskB(a){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const res = a * 2;
resolve(res);
},1000)
})
}
function taskC(a){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const res = a * -1;
resolve(res);
},2000)
})
}
taskA(5,1).then((a_res)=>{
console.log('A result : ',a_res);
return taskB(a_res);
}).then((b_res)=>{
console.log('B result : ',b_res);
return taskC(b_res);
}).then((c_res)=>{
console.log('C result : ',c_res)
})
위 처럼 then메서드를 계속 이어서 붙이는것을 thenchaining 덴체이닝 이라고 함,
taskA의 결과값인 a_res를 인자로 전달한 taskB에 또 덴 메서드를 이용하고 그 결과값인
b_res를 taskC의 인자로 전달하고 taskC에 또 덴 메서드를 붙여서 마지막까지 호출하는 방식을 사용함.
위 사진처럼 위의 덴체이닝을 사용한 코드는 코드를 계속 아래로 늘여쓸수있고,
아래처럼 사용을 하게되면 코드가 한줄씩 들어가게되는 콜백지옥을 겪게된다.
위 덴체이닝처럼 사용을하면 중간에 끊어서 다른 코드를 넣을수도있음
const bPromiseResult = taskA(5,1).then((a_res)=>{
console.log('A result : ',a_res);
return taskB(a_res);
});
console.log('qweqweqwe');
console.log('123123');
console.log('cocococo');
bPromiseResult.then((b_res)=>{
console.log('B result : ',b_res);
return taskC(b_res);
}).then((c_res)=>{
console.log('C result : ',c_res)
})
이런식으로 bPromiseResult를 변수에 담아놓고 아래에 연결해둔 뒤 중간에 콘솔을 출력하는 코드를 넣어도 콘솔이 출력된 뒤 다시 A결과 B결과 C결과가 나오는걸 볼 수 있음
좀 더 가독성있게끔 정리가 가능하다. 라는걸 알아두면 좋을듯하다.
Promise는 지금 배운 기본이 가장 어렵다고한다.