ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JavaScript] Promise에서 한 걸음 더, async & await
    JavaScript 2022. 2. 15. 04:12
    728x90

     

    Promise에서 만족하지 못하는 상황에서 비동기 처리를 하기 위해 더 편리하고 유용한 키워드인 async와 await에 대해 알아보겠다.

     

    Promise에 대한 이해가 부족하다면 아래의 포스팅을 참고하길 바란다.

     

    비동기와 Promise

     

    [JavaScript] 비동기와 Promise

    비동기라는 단어 자체가 생소할 수도 있고, 몇 번 들어본 경우도 있을테지만 정확히 어떤 개념이고, 동작 흐름에 대해서설명해보라 한다면 쉽지 않았다. 이번 계기로, 비동기 처리에 대해 확실

    6ro-29.tistory.com

     

     

    다음의 코드를 보면 비동기 처리를 하기위해 콜백함수가 중첩적으로 구현되어 있는 걸 볼 수 있는데 이러한 방식으로 구현하게 되면 복잡성도 높아질 것이며, 소위 말해 콜백지옥에 빠지게 된다. 또한 기존 Promise를 이용했던 것과 달리 한 눈에 동작을 파악하기 쉽지 않아 보인다.

     

    timer(1000, function(){
        console.log('start');
        timer(1000, function(){
        	console.log('start');
            timer(1000, function(){
        		console.log('start');
        	});
        });
    });

     

    이에 대한 해결방안으로 Promise의 then, catch가 사용되는데 각각의 작업들이 체이닝되어 복잡성도 줄어들고 콜백지옥에서 벗어날 수 있게 된다.

     

    timer(1000)
        .then(function(){
            console.log('start');
            return timer(1000);
        })
        .then(function(){
            console.log('start');
            return timer(1000);
        })
        .then(function(){
            console.log('end');
        })

     

    하지만 Promise를 사용할 때에도 매번 콜백함수를 명시해줘야 한다는 점과 여전히 코드를 작성하는데에 있어서 잦은 실수의 가능성이 있는 걸로 보인다. 

     

    이를 동기적인 처리를 할때처럼 간단히 작성하고 싶고, 비동기적 작업과 똑같이 수행되게끔 async, await 가 등장한 것이다. 아래의 코드를 통해 문법적으로 굉장히 단순해지는 것을 파악할 수 있다.

     

    async function run() {
        await timer(1000);
        console.log('start');
        await timer(1000);
        console.log('start');
        await timer(1000);
        console.log('end');
    }

     

    await은 비동기 함수 앞에 붙는 키워드로 해당 함수가 실행되길 대기하라는 의미에서 사용되며, 이 키워드가 붙어있고 Promise를 리턴해주는 함수라면 반드시 다른 함수의 내부에서 실행되어야 한다.

     

    이때, 이 내부를 감싸주는 함수는 맨 앞에 async를 붙여줌으로써, 일반적인 함수가 아닌 Promise를 반환하는 비동기적인 함수라는 걸 명시해주어야 한다. 덧붙여 await이 붙은 코드는 async 함수 안에 없는 경우 에러가 발생한다.

     

    따라서, async, await은 이전에 Promise를 사용했던 비동기 함수를 조금 더 보기 편한 동기적인 코드로 보이게 만들어주는 도구라고 생각하면 될 것이다.

     

     

    마지막으로 위에서 배운 내용을 토대로 Promise를 반환하는 timer 함수를 구현해 async와 await을 사용하는 코드를 간단히 살펴보자.

     

    function timer(time) {
        return new Promise(function(resolve) {
            setTimeout(function(){
                resolve(time);
            }, time);
        });
    }

     

    time을 인자로 받아 Promise 객체를 반환해주는 타이머 함수인데, setTimeout으로 time에 해당하는 시간이 지난 후에 resolve가 호출되어 비동기적으로 동작하는 코드인 것을 확인할 수 있다.

     

     

    timer 함수를 활용했을 때 기존 Promise의 함수를 사용하는 경우와 async, await을 쓰는 경우로 나눠보면 다음과 같다.

     

    console.log('start');
    timer(1000)
        .then(function(time){
            console.log(`time: ${time}`);
            return timer(time+1000);
        })
        .then(function(time){
            console.log(`time: ${time}`);
            return timer(time+1000);
        })
        .then(function(time){
            console.log(`time: ${time}`);
            console.log('end');
        });

     

    start부터 end까지 1초가 지날때마다 ms 단위의 시간을 출력하는 코드이며, Promise 객체를 반환하는 timer가 then의 체이닝을 통해서 콜백함수가 계속하여 작성된 것을 볼 수 있다. 참고로, 체이닝이 가능한 이유는 첫번째 then에서부터 똑같이 Promise를 반환하기때문에 가능한 것이다.

     

     

    위에서는 비교적으로 봤을 때 똑같은 콜백함수를 다시 작성해야 한다는 번거로움과 코드가 더 복잡해진다면 동작 방식을 한 번에 이해하기는 쉽지않을 것이다.

     

    async function run() {
        console.log('start');
    
        var time = await timer(1000);
        console.log(`time : ${time}`);
        time = await timer(time+1000);
        console.log(`time : ${time}`);
        time = await timer(time+1000);
        console.log(`time : ${time}`);
        
        console.log('end');
    }
    run();

     

    run이라는 함수를 호출했을 때 async가 붙어 비동기 함수라는 걸 알 수 있다. Promise를 반환하는 timer의 값을 time 변수에 받아 초가 지날때마다 출력해주는데, 내부적으로는 비동기적으로 동작하나 코드상 동기적으로 동작하는 코드인 것처럼 간단해보이고 코드의 복잡성이 증가하더라도 이해하는데 있어 수월해보인다.

     

     

    결과를 보면 start 다음에 Promise가 출력되는데 이는 함수 앞에 async가 붙었을 때 암시적으로 Promise를 반환한다는 사실을 확인할 수 있다.

     

     

    이를 통해 Promise를 반환하는 비동기 함수라면 async가 붙은 run 함수를 호출할 때에도 await이 붙을 수 있을 것이다. 결국, await이 붙었으니 또 async 함수 내부에 작성되어야 하는 것과 한다. 이렇듯 비동기 처리를 여러번 할 수 있고 작업을 끝내려면 이전에 사용했던 Promise 객체의 then을 활용해서 작업의 마침을 알려줄 수도 있다.

     

     

    자바스크립트에서 비동기의 꽃이라 불리는 async와 await은 앞으로도 유용하게 쓰이겠지만 시간이 흐르면서 더 나은 새로운 기술이나 단점을 보완한 문법들이 나올지 누구도 예상할 수 없을 것이다. 개인적으로 자바스크립트 비동기 처리의 학습을 해오면서 배움과 편리함에 대한 인간의 욕심은 끝도 없다는 걸 느꼈다.

     

     

     

    728x90

    'JavaScript' 카테고리의 다른 글

    [JavaScript] 비동기와 Promise  (0) 2022.02.13
    [JavaScript] 클로저(Closure)란?  (0) 2022.02.10
    [JavaScript] 편리함을 위한 ES6  (0) 2022.02.04
    [JavaScript] ES6 Module  (0) 2022.02.04

    댓글

Designed by Tistory.