본문 바로가기

JavaScript & jQuery

<javascript> 비동기 처리 (Promise, async, await)

반응형

자바스크립트에서 비동기 처리동기적 처리와 비교해서 얘기를 할 수 있겠는데

 

 

위 사진은 동기적처리에서의 작업연산 방식과 비동기적으로 처리 했을 때의 작업 연산 방식에 대해 나타내고 있는 사진이다.

 

동기적으로 작업을 처리하려 할 떄는 1번이 끝날 때 까지 2번은 대기 후 실행, 2번이 끝날 떄 까지 3번이 대기 후 실행식으로 이루어지게 된다. 

 

하지만 비동기적으로 작업을 처리하게 되면 우리가 코드를 실행하면서 흐름이 끊기지 않게 된다. 1번실행이 안끝나도 2번,3번, 4번 작업이 이루어지게 된다.   

 

위 사진으로 예를 들어 정리하면 

동기적 처리 : 4명이 한팀인 계주 달리기 (바통주자가 와야지 다음주자가 뛰기 가능!!)

비동기적 처리 : 4명이서 100m 달리기 (요이 땅 하면 동시에 출발가능!!)

 

정도로 생각하고 차이점이 무엇인지 정도만 알고 있으면 되겠다.

코드를 보면서 한번 차이점을 알아보자~~

 

function test() {
    const start = Date.now(); // 현재날짜를 숫자형태로 표시 
    for(let i = 0; i<100000000; i++){

    }
    const end = Date.now();
    console.log(end-start + 'ms'); // 작업을 처리하는데 얼마나 걸린지 알게 해주는 함수
}

test();
console.log("다음 주자");

 

 

이런식으로 순서대로 test() 함수를 호출해주고 난 다음에 다음 주자를 출력해주는 함수인데 test() 함수내에서 반복문을 실행하는 작업이 끝나기 전에는 다음 주자를 출력해주지 않고 137ms 초 동안 작업을 끝내고 나서야지 찍히는 것을 볼 수가 있다. 

 

 

function test() {
    setTimeout(function (){
        const start = Date.now(); // 현재날짜를 숫자형태로 표시 
        for(let i = 0; i<100000000; i++){
    
        }
        const end = Date.now();
        console.log(end-start + 'ms');
    },0)
}
console.log("이전 주자");
test();
console.log("다음 주자");

 

위의 방식은 setTimeout() 함수를 이용해서 비동기적으로 처리 할 수 있게 한 것인데 차이점이 바로 보일 것이다.

 

setTimeout() 을 써주게 되면 우리가 특정 작업을 백그라운드에서 실행을 하기 때문에 코드흐름을 막지 않고 동시에 다른 작업을 이어 갈 수 있게 해준다. 뒤에 '0' 이 들어간 자리는 본인이 정해줄 수 있는데 몇 ms 뒤에 작업을 실행시키겠다 라고 초 단위를 정해주는 부분이다. 

원래동기적으로 처리 했다면 이전주자, xxx(ms), 다음주자 순으로 출력이 되었겠지만 작업수행을 백그라운드에서 따로 빼줘서 실행을 시켰기 때문에 위와 같은 결과를 나타내는 것을 볼 수가 있을 것이다.  

 

 

이런 비동기 작업을 도와주는게 es6 에 나온 Promise 인데 한번 알아보자. 우리가 setTimeout 을 이용해서 비동기작업을 끝낸뒤에 어떤 작업을 실행시킬 떄는 콜백함수로 처리하는데  콜백함수의 콜백함수로 계속 흔히 '콜백지옥' 이라는 코드를 경험하게 될 것이다.  

 

 

function increaseAndPrint(n , callblack){
    setTimeout(() => {
        const increased = n + 1;
        console.log(increased);
        if(callblack)
        callblack(increased);
    }, 1000)
}

increaseAndPrint(0, n => {
    increaseAndPrint(n, n=>{
        increaseAndPrint(n, n=> {
            increaseAndPrint(n, n=> {
                increaseAndPrint(n, n=> {
                    console.log('계산 끝..')
                })
            })
        })
    })
});

 

이렇게 1초에 1씩 증가되서 계산 되는 함수를 만들어봤는데 보다시피 콜백의 콜백을 호출하는 것을 볼 수 있다. 

 

 

function increaseAndPrint(n) {
    return new Promise((resolve, reject) =>{
        setTimeout(()=>{
            const value = n + 1;
            if(value === 5){
                const error = new Error();
                error.name = 'value is five!!';
                reject(error);
                return;
            }
            console.log(value);
            resolve(value);
        },1000);
    })
}

increaseAndPrint(0).then(n => {
    return increaseAndPrint(n);
}).then(n => {
    return increaseAndPrint(n);
}).then(n => {
    return increaseAndPrint(n);
}).then(n => {
    return increaseAndPrint(n);
}).then(n => {
    return increaseAndPrint(n);
}).catch(e => {
    console.error(e);
});

 

위의 코드는 Promise 를 이용해서 코드를 바꾸어 본것인데 파라미터로

rejectresolve == 성공헀을때 와 실패했을 때의 작업을 나타내느 값읋 받아주고 콜백함수안에 콜백함수를 지정해주는 것이 아니라 then() 메소드를 이용해서 작업을 수행시켜주는 것을 볼 수 있다. catch() 는 이제 reject (실패)했을 경우에 에러를 만들어내고 그 에러를 잡아 줄때 써주는 메소드인데 reject, resolve 같이 then 과 catch 세트로 알아두면 편하겠다. 

 

Promise를 써줌으로써 보다 간결하고 비동기작업의 개수가 많아져도  코드의 깊이가 깊어지지 않게 하는 것을 확인 할 수가 있는데 반대로 불편한 점이 있는데

 

1.에러를 잡을 때 어떤 부분에서 에러가 발생했는지 파악하기 어렵다.

2.특정 조건에 따라 분기를 나누는 작업도 어렵다  (then 으로 계속 작업해주기 때문)

3. 특정 값을 공유해가면서 작업해 나가기가 번거롭다.

 

정도로 알아 두면 되겠당><!

 

 

*****************비동기 처리를 도와주는 것이 Promise 고 그 Promise 를 더 쉽게 써주게 도와주는 것이 있는데 바로 asyncawait 문법이다. *****************

 

function sleep(ms) {
    return new Promise(resolve =>
         setTimeout(resolve, ms));
}

async function process() {
    console.log('ㅎㅇㅎㅇ');
    await sleep(1000);
    console.log('어웨잇 개꿀!');
}
process();

이런 식으로 async 키워드를 함수 앞에 붙여주면 되고 await 은 Promise 앞에 붙여주게 되서 굳이 .then 을 통해서 호출해줄 필요가 없게 되고 작업의 분기점을 만드는 것도 보다 쉬워지게 된다.

 

기존 reject 와 catch 를 이용해서 에러를 잡아내는 것처럼 await 와 async 를 이용해서 간단히 에러를 잡아내보는 것도 해보자 

 

function sleep(ms) {
    return new Promise(resolve =>
         setTimeout(resolve, ms));
}

async function makeError(){
    await sleep(1000);
    const error = new Error();
    throw error;
}

async function process() {
try {
    await makeError();
}   catch (e){
    console.error(e);
} 
}
process();

 

이런식으로 makeError 함수에서 에러를 던져주고 (thorw) try, catch 문을 이용해서 에러를 호출해주면 된다.

 

동기적 처리 와 비동기적 처리, 비동기적 처리를 더 쉽게 도와주는 것이 Promise, 그 Promise 를 더 쉽게 사용하게 해주는 문법이 async, await 라고 간단히 정리 하면 되겠다!

반응형