본문 바로가기
JavaScript&Typescript

비동기 처리 -2

by 스르나 2021. 1. 28.
  1. Promise
  2. async await

 

1.Promise

Promise는 비동기 처리를 실행하고 그 처리가 끝난 뒤 다음 처리를 실행하기 위한 용도로 사용하는 비동기 처리 함수이다.

 

Promise의 기본형태는 아래와 같다

 

Promise는 함수가 아니랄 자바스크립트 객체이다. Promise안의 function에 비동기적으로 처리할 작업을 추가하면 된다.

 

코드를 바로한번보자

 

const worke=new Promise(function(resolve,reject){
    setTimeout(function(){
        console.log('A')
        var num=parseInt(prompt('숫자를 입력하세요'))
        if(num>10){
            resolve('10이 넘어갔다')
        }
        else{
            reject('10을 못 넘었다')
        }
    },1000)
})

//실행
work.then(function(str){
    console.log(str);
}).catch(function(errorMsg){
    console.log(errorMsg);
})

위 코드는 Promise객체안에 setTimeout을 이용해 비동기 처리를 하는 작업을 넣은 것이다. 코드를 차근차근 설명하면 우선 new Promise로 Promise객체를 생성한다. 그리고 생성한 Promise(work)에 then함수를 호출해서 실행을 하는 것이다.

 

then을 호출하면 Promise안의 함수가 실행이된다. 이때 resolve와 reject가 있는데 resolve는 비동기처리가 정상적으로 실행이 되었을때 호출이된다. 그리고 reject는 비동기처리가 실패했을때실행된다. 또한 resolve,reject를 호출할때 넘겨준 인자는 각각 then,catch안의 함수의 인자로 넘어간다.

 

여기서는 '10이 넘어갔다'라는 문자열이 then안의 function의 str인자로 넘어가는 것이다.

그다음으로는 reject의 인자는 catch안의 함수의 인자로 넘어간다 여기서는 '10을 못 넘었다'라는 문자열이 catch안의 함수의 인자로 넘어간다.

 

즉, then은 Promise의 비동기작업이 성공적으로 실해이 되었을대 호출이 되고, catch는 비동기적업이 실패했을대 실행이 된다.

 

추가로 보면 then뒤에 catch를 체이닝한것을 볼 수 있는데 then의 return값이 Promise이기 때문이다.

 

then의 return이 Promise라는 것은 then안에 다른 비동기 처리를 위한 함수를 넣을 수 도 있다는 것을 의미한다.

 

const work=new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(10)
    },1000)
})

work
.then(num=>num*2)
.then(num=>num+5)
.then(num=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{            
            resolve(num-4)
        },1000)
    })
})
.then(num=>{console.log(num)})
.finally(()=>console.log('무조건 실행!'))

then안에 new Promise를 통해 새로운 Promise객체를 만들고 그안에서 다시 resolve를 동해 값을 리턴해주는 과정을 볼 수 있다.

 

여기서 보면 then은 숫자,문자열 같은 변수를 넘겨줄 수도 있고 Promise를 넘겨줄 수도 있다.

 

다음으로는 then과 catch외에 새로 추가된 Promise의 함수 finally가 있다. finally는 Promise의 비동기 처리가 성공이던 실패이던간에 무조건 실행되는 함수이다.

 

추가로 Promise를 선언할때 유의해야 할것이 있다.

 

아래의 코드를 보자

const hello=new Promise((resolve,reject)=>{
    console.log('hello');
}).then(console.log('after'));

const hi= ()=>{
    return new Promise((resolve,reject)=>{
        console.log('hi');
    })
}

코드를 보면 위의 hello는 Promise객체를 바로받은것이고 밑의 hi는 Promise를 리턴하는 함수인데 위의 코드를 실행하면 아래와 같이 나온다.

차이점이 보이는가? 위의 hello는 바로 실행이 된다. 즉, Promise는 선언만 하면 바로 실행이되는 것이다 이점을 유의해서 Promise를 사용해야한다. 다음에 알아볼 비동기처리를  병렬로 처리하는 Promise.All에서 차이가 나기 때문이다.

2. async, await

다음으로 알아볼것은 async,await이다.

 

비동기 처리를 위해 지금까지본 setTimeout, Promise모두 생각 보다 복잡한 문법이고, 코드가 길어질수록 가독성이 많이 떨어지는 단점을 가지고 있다. 더욱 큰 문제는 소위 콜백지옥이라고 부르는 콜백함수들을 처리하는 과정이 매우 길어질수록 코드가 심각하게 복잡해지는 문제가 있다.

 

그래서 자바스크립트에서는 이문제를 해결하기 위해 async,await를 만들었다.

 

우선 자바스크립트 공식 문서의 async,awati의 설명을 보면 이렇게 나와 있다.

 

async 함수에는 await식이 포함될 수 있습니다. 이 식은 async함수의 실행을 일시 중지하고 전달 된 Promise 의 해결을 기다린 음 async함수의 실행을 다시 시작하고 완료후 값을 반환합니다.

 

설명을 보고 이해하기 힘들다면 아래의 코드와 실행결과를 보자

 

const work=()=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('작업!')
            resolve('완료!')
        },2000)
    });    
}

async function foo(){
    console.log('시작')
    const response= await work();
    console.log('끝')
    console.log(response)
}

foo()

 

실행 결과

 

위의 코드를 보면 async함수인 foo함수의 내용에서 먼저 시작이라고 콘솔에 찍어준뒤 await문장이 실행되는데 공식 문서 설명대로 await문이 실행되었기 때문에 foo함수는 실행을 중단하고 await문장의 Promise가 실행되기를 기다린다.

 

위의 work Promise함수는 2초뒤에 작업이라고 콘솔에 찍어주고 resolve로 '완료!'라는 문자열을 반환한다.

 

여기까지 보면 이게 뭐가 편리한지 이해가 가지 않을 수 있다.

 

그렇다면 다음과 같은 예시를 생각해보다 비동기처리 A,B,C가 있을때 A->B->C순으로 처리를 해야한다고 해보자 이 작업을 위해 Promise는 다음과 같은 방식으로 처리를해야한다.

 

const work=(num)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('작업 1')
            resolve(num+1)
        },2000)
    });    
}

const work2=(num)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('작업 2')
            resolve(num+2)
        },2000)
    })
}

const work3=(num)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('작업 3')
            resolve(num+3)
        },2000)
    })
}
work(10)
.then(num=>work2(num))
.then(num=>work3(num))
.then(num=>{console.log(num)})

 

위의 코드는 간단하지만 then안의 내용이 복잡해지고 비동기처리의 양이 많아지고 각 비동기처리함수들의 연계가 긴밀해진다면 코드는  복잡해진다.

 

하지만 이런 코드를 async와 await를 이용하면 아래와 같이 바뀐다.

 

 

const work=(num)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('작업 1')
            resolve(num+1)
        },2000)
    });    
}

const work2=(num)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('작업 2')
            resolve(num+2)
        },2000)
    })
}

const work3=(num)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('작업 3')
            resolve(num+3)
        },2000)
    })
}

const foo=async ()=>{
    const data1=await work(10);
    const data2=await work2(data1);
    const data3=await work3(data2);

    console.log(data3);
}

foo();

foo함수에서 await문은 위에서 부터 순차적으로 진행된다. 마치 일반함수 처럼말이다.

 

이렇게 async,await를 이용하면 비동기처리를 진행할때 훨신더 깔끔한 코드를 만들 수 있다.

 

마지막으로 async,await의 에러처리는 아래와 같다.

 

const foo1=()=>{
    return new Promise((resolve,reject)=>{
        throw 'error';
    })
}

const foo2=()=>{
    return new Promise((resolve,reject)=>{
        console.log('hello')
    })
}

const main=async ()=>{
    try{
        const data=await foo1();
        const data2=await foo2();
    }catch(e){
        console.log(e)
    }
}

main()

마지막 main함수를 보면 에러처리를 try,catch문으로 해결했다. async,await의 에러처리는 일반 코드와 같이 try,catch로 해결한다.

 

이렇게 async,await를 보니 우리가 사용하는 일반 코드와 별 차이가 없게 느껴진다. 그러므로 기존의 비동기 처리를 위한 Promise,setTimeout보다 훨신 좋아보인다. 하지만 각각의 용도가 더욱 알맞는 경우가 있으니 적절하게 사용해야한다.

'JavaScript&Typescript' 카테고리의 다른 글

비동기 처리-3  (0) 2021.01.30
비동기 처리-1  (0) 2020.12.18