일반적인 비동기를 알아보기 위한 간단한 코드입니다.
비동기 예시
function delay(sec, callback){
setTimeout(()=> {
callback(new Date().toISOString());
},sec*1000);
}
console.log('start', new Date().toISOString());
delay(1,(result)=>{
console.log(1,result);
});
console.log('hello');
만약 1초의 간격을 두고 표현해 주고싶다고 아래와 같은 코드를 작성하였을 경우 비동기 상황에선 거의 동시게 시간이 찍히게 됩니다.
function delay(sec, callback){
setTimeout(()=> {
callback(new Date().toISOString());
},sec*1000);
}
delay(1,(result)=>{
console.log(1,result);
});
delay(1,(result)=>{
console.log(2,result);
});
delay(1,(result)=>{
console.log(3,result);
});
그래서 그 다음으로 가장 쉽게 생각한 부분이 콜백함수 안에 또 콜백, 그 안에 또 콜백은 만드는 과정이 될겁니다.
function delay(sec, callback){
setTimeout(()=> {
callback(new Date().toISOString());
},sec*1000);
}
delay(1,(result)=>{
console.log(1,result);
delay(1,(result)=>{
console.log(2,result);
delay(1,(result)=>{
console.log(3,result);
});
});
});
하지만, 콜백함수를 고려하여 아래와 같은 코드로 작성하였을 경우 코드를 읽거나 이해하기에 많은 어려움이 있을겁니다.
function delay(sec, callback){
setTimeout(()=> {
callback(new Date().toISOString());
},sec*1000);
}
delay(1,(result)=>{
console.log(1,result);
delay(1,(result)=>{
delay(1,(result)=>{
console.log(3,result);
});
//콜백을 고려하여 아래에 작성해줬을 경우!!
console.log(2,result);
});
});
이러한 콜백함수들의 지옥!? 을 해결해기 위해 나온것들이 Promise async&await 입니다.
위의 코드를 promis 형태로 바꿔보겠습니다.
function delay(sec, callback){
setTimeout(()=> {
callback(new Date().toISOString());
},sec*1000);
}
delay(1,(result)=>{
console.log(1,result);
});
function delayP(sec){
// resolve : 할일을 다했을떄 호출 rejct : 할일을 하다가 오류가 발생했을떄
return new Promise( (resolve, rejct) => {
setTimeout(()=>{
resolve(new Date().toISOString());
},sec*1000);
});
}
delayP(1).then((result)=>{
console.log(1,result);
return delayP(1);
}).then((result)=>{
console.log(2,result);
return delayP(1);
}).then((result)=>{
console.log(3,result);
});
위 코드에서는 마지막 3초 이후에 아무것도 리턴하지 않았습니다.
추가로 return 'finish' 를 하게 된다면 Promise 가 아니기 때문에 3초의 결과가 찍힘과 동시에 finish가 찍히게 됩니다.
우선 아래의 코드를 살펴봅시다.
아무것도 적지 않은 함수를 하나 만들고, Async가 앞에 적힌 함수를 하나 만들었습니다. 반환값은 어떻게 될까요?
아무것도 적지 않은 함수는 func
를 반환하지만
async를 적은 함수는 promise
를 반환합니다.
function myFunc(){
return 'func';
}
async function myAsync(){
return 'async';
}
console.log(myFunc());
console.log(myAsync());
기존 Promise 코드와 async를 비교해보겠습니다.
Promise는 promise를 리턴해주기 위해 명시적으로 new Promise를 리턴하는 것을 만들어 줬어야 했습니다.
하지만, async
를 사용하면 자동으로 promise를 리턴하게 됩니다. 여기서 promise
는 똑같이 비동기 연산을 다루되 연산이 끝났을 때 resolve
연산에 에러가 났을때 reject
함수를 실행했다면 async
함수는 연산이 끝났을 때 리턴만 해주면 되고 에러가 났을 경우 함수안에서 draw 해주면 됩니다.
function delayP(sec){
// resolve : 할일을 다했을떄 호출 rejct : 할일을 하다가 오류가 발생했을떄
return new Promise( (resolve, rejct) => {
setTimeout(()=>{
resolve(new Date().toISOString());
},sec*1000);
});
}
async function myAsync(){
return 'async';
}
//async의 result는 async 함수의 리턴값입니다.
myAsync().then((result)=>{
console.log(result);
});
여기서 await
는 어떻게 사용하는지 아래의 코드에서 살펴보겠습니다.
awiat
를 사용하게 되면 아래 코드와 같이 async
함수 안에서 delayP를 3초 기다리고 async를 반환하는 코드를 만들 수 있습니다.
function delayP(sec){
// resolve : 할일을 다했을떄 호출 rejct : 할일을 하다가 오류가 발생했을떄
return new Promise( (resolve, rejct) => {
setTimeout(()=>{
resolve(new Date().toISOString());
},sec*1000);
});
}
async function myAsync(){
//아래와 같이 하나의 객체로 받을 수도 있습니다.
const time = await delayP(3);
console.log(time);
return 'async';
}
//async의 result는 async 함수의 리턴값입니다.
myAsync().then((result)=>{
console.log(result);
});
await
에 대해 더 알아봅시다.
아래의 두 코드와 같이 최종적으로 반환되는 값이 promise
로 반환됩니다.
async function myAsync(){
//아래와 같이 하나의 객체로 받을 수도 있습니다.
const result = await delayP(3).then((time=>{
return 'x"
});
console.log(result);
return 'async';
}
또한, promise
로 작성한 함수가 아닌 일반함수도 await
를 앞에 붙여주게 되면 똑같이 작동합니다.
function normalFunc(){
return 'wow';
}
async function myAsync(){
//아래와 같이 일반 함수도 가능합니다.
const result = await normalfunc();
console.log(result);
return 'async';
}
promise
/async&await
는 비동기 상황의 코드의 로직을 우리가 보기 편하게 표현하기 위해를 조화롭게 사용합니다. 한가지에 장점이 있고 단점이 있는게 아니라 promise
가 편한 방법이 있고 async&await
가 편한 방법이 있습니다. 또, 비동기를 처리할때 중요한것 하나가 비동기에서의 예외처리입니다. 이 부분은 따로 다뤄보도록 하겠습니다.