<!-- [코드 7-1-1] App.svelte-->
<script>
function a(callback) {
setTimeout(()=>{
console.log(1);
callback();
},1000);
}
function b(){
console.log(2);
}
a(b);
</script>
어떤 함수의 인수로 다른 함수를 넣어 순서를 보장할 수 있는데, 이 때 인수로 전달된 함수를 callback이라고 한다.
<!-- [코드 7-1-2] App.svelte-->
<script>
function a(callback) {
setTimeout(()=>{
console.log(1);
callback();
},500);
}
function b(callback) {
setTimeout(()=>{
console.log(2);
callback();
},500);
}
function c(callback) {
setTimeout(()=>{
console.log(3);
callback();
},500);
}
function d(callback) {
setTimeout(()=>{
console.log(4);
callback();
},500);
}
a(()=>{
b(()=>{
c(()=>{
d(()=>{
cosole.log(5);
})
})
})
})
</script>
예상되는 문제로 콜백지옥이 있다. 콜백패턴이 여러 번 나타날 때 7-1-2코드처럼 가독성을 크게 해치는 복잡한 코드가 작성되는 것을 콜백지옥이라고 한다.
<!-- [코드 7-2-1] App.svelte-->
<script>
function first(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('하나');
resolve();
}, 500);
});
}
function second(){
console.log('둘');
}
first().then(second);
</script>
함수 실행순서를 보장하기 위한 다른 방법으로 Promise가 있다. callback패턴과 비교해보면, callback이 들어갈 위치에서 resolve()를 호출하는 것을 확인할 수 있다.
resolve는 Promise객체를 반환하는 역할을 한다. resolve로 Promise가 반환된 시점에 then의 인수(함수)를 실행한다.
<!-- [코드 7-2-2] App.svelte-->
<script>
function first(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('하나');
resolve();
}, 500);
});
}
function second(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('둘');
resolve();
}, 500);
});
}
function third(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('셋');
resolve();
}, 500);
});
}
function four(){
console.log('야');
}
first()
.then( ()=>second() )
.then( ()=>third() )
.then( ()=>four() );
</script>
순서를 보장할 함수가 많아지는 경우에도 then chain패턴을 적절히 정리하면 콜백지옥과 같은 가독성저하 문제를 피할 수 있다.
<!-- [코드 7-2-3] App.svelte-->
<script>
function first(wantReject){
return new Promise((resolve, reject)=>{
if (wantReject) {
reject(new Error('리젝받아라'));
} else {
resolve('리졸브받아라');
}
});
}
first(true)
.then((res)=>{
console.log('둘', res);
})
.catch((err)=>{
console.log(err.message);
}) //"리젝받아라" 출력
.finally(()=>{
console.log('무조건출력');
});
</script>
정상적으로 resolve되지 못한 경우 reject()를 통해 메시지와 함께 reject된 Promise객체를 반환할 수 있다. 이는 catch chain으로 받을 수 있다.
가장 뒤에 붙어있는 finally는 resolve되었든 reject되었든 마지막에 실행할 함수를 인수로 받는다.
Promise는 3가지 상태가 있다. (Pending, Fulfilled, Rejected). resolve된 뒤는 Fulfilled, reject된 뒤는 Rejected, 그리고 resolve/reject되기 전 초기상태를 Pending이라 한다.
<!-- [코드 7-3-1] App.svelte-->
<script>
function first(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('하나');
resolve();
}, 500);
});
}
function second(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('둘');
resolve();
}, 500);
});
}
function third(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('셋');
resolve();
}, 500);
});
}
function four(){
console.log('야');
}
async function run(){
await first();
await second();
await third();
await four();
}
run();
</script>
어떤 함수가 Promise를 반환하는 경우, async함수 내에서 await키워드를 붙여 호출하면 마치 Promise then chain을 붙인 것 처럼 실행순서를 보장할 수 있다.
<!-- [코드 7-3-2] App.svelte-->
<script>
function first(wantReject){
return new Promise((resolve, reject)=>{
if (wantReject) {
reject(new Error('리젝받아라'));
} else {
console.log('하나');
resolve('리졸브받아라');
}
});
}
async function run(){
try{
const res = await first(true);
console.log('둘', res);
} catch(err){
console.log(err.message);
} finally {
console.log('무조건 출력');
}
}
run();
</script>
Promise의 then-catch-finally패턴은 async-await를 사용하고자 할때는 try-catch-finally패턴으로 대체할 수 있다.
<!-- [코드 7-1] App.svelte-->