탱구탱구 개발자 일기

앞서 자바스크립트 비동기 처리방법으로 Promise에 대해 설명했었다. Promise는 비동기 처리 이후 then 메서드를 사용해 새로운 Promise 객체를 반환하여 다음에 이어갈 작업들에 대해 동기적으로 프로그래밍을 하였다.

예제

function buySomething(budget) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let payment = parseInt(prompt("얼마를 지불하시겠습니까?"));
            let balance = budget - payment;
            if (balance > 0) {
                console.log(`${payment}원을 지불하였습니다.`);
                resolve(balance);
            } else {
                reject(`잔액이 부족합니다. 잔액은 ${budget}원 입니다.`);
            }
        }, 1000);
    });
}

buySomething(1000)
.then((balance) => {
    return buySomething(balance);
})
.then((balance) => {
    return buySomething(balance);
})

 

Promise는 callback 함수를 인수로 넘겨 호출하는 것을 반복하는 콜백 지옥을 해결할 수 있었지만 then 메서드를 chain으로 계속 연결해줘야 하는 점과 코드가 복잡해질 수 있고 프로미스에서 에러가 발생할 때 디버깅이 힘들다는 점이 있었다.

Async & Await

Async와 Await는 ECMAScript 2017 이후에 등장한 새로운 비동기 처리 연산자이다. 단, 이 방법들은 기본적으로 Promise를 기반으로 하고있기 때문에 Promise에 대해 먼저 알아야한다. Promise는 이 글을 참고하자. 

 

앞서 말한 것처럼 Promise를 사용해도 여전히 코드가 복잡한 부분이 있었다. Async와 Await를 사용하면 비동기 처리코드를 간결하게 동기적으로 처리할 수 있다.

Async

Async 연산자는 기존의 함수 선언문을 비동기 처리 함수를 정의하는 선언문으로 만들어 준다. 그 함수는 Async function 객체를 반환하는 비동기 함수가 되며 암시적으로 Promise를 사용하여 결괏값을 반환한다.

 

예제

// async&await

let promise = (async function () {
    //throw new Error("에러 발생");
    return "promise";
})();

console.log(promise);

 결과

Promise {<resolved>: "promise"}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "promise"

변수 promise는 즉시 실행함수 안에 정의된 async function 선언문을 참조값으로 가지고 있다. 변수를 실행하면 즉시 실행 함수가 호출되며 Promise의 상태는 resolved이며 리턴값에 정의한 primitive type인 문자열 promise를 Promise.resolve로 감싸서 반환한다.

위 코드에서 따로 Promise 객체를 생성하지 않았지만 async 키워드가 내부적으로 Promise를 사용하여 결괏값을 반환시켜주는 것이다.

   

반면에 위에 에러를 발생시키는 코드를 주석 해제하고 동일하게 실행하면 아래와 같은 결과가 나타난다.

Promise{<rejected>: Error: 에러 발생}
__proto__: Promise
[[PromiseStatus]]: "rejected"
[[PromiseValue]]: Error: 에러 발생

Promise의 상태는 rejected되었고 Promise의 반환값은 error이며 해당 에러 내용이 들어가있다.

 

 Async로 정의한 것을 Promise를 사용한다면 아래와 같은 코드가 될 것이다.

function promise(boolean) {
    return new Promise((resolve, reject) => {
        if (boolean == true) {
            resolve("promise");
        } else {
            throw new Error("에러 발생");
        }
    })
}

promise(true)
    .then() // true 일 때
    .catch(); // false 일 때

Await

 

Await 연산자는 Promise를 기다리기 위해 사용한다. 이 때 기다린다는 의미는 Promise 처리가 fulfill(이행)되던지 reject(거절)되던지를 의미한다. 해당 처리가 끝나고 난 뒤 다시 async 함수가 실행된다. 즉, await를 붙인 표현식이 처리가 되고 다음 코드를 실행시킨다. 그러나 await를 붙인 표현식이 Promise 객체를 반환하지 않는다면 

 

await 연산자는 반드시 async function 안에서만 사용할 수 있다.

 

async & await 연산자가 동작하지 않는 예제

 

아래 예제는 async 함수 안에 await 연산자를 통해 비동기 함수를 동기 처리하려고 한다. 원하는 실행 결과는 timeRecode 함수의 코드 순서대로 처리되는 것이다. 그러나 결과는 원래 비동기 처리가 적용되었다. 

async function timeRecode() {
    console.log("Start");
    await checkTime1();
    await checkTime2();
    await console.log("End");
}

function checkTime1() {
    setTimeout(() => {
        console.log("checkTime1:", Date.now());
    }, 3000);
}

function checkTime2() {
    setTimeout(() => {
        console.log("checkTime2:", Date.now());
    }, 1000);
}

 

결과

Start
End
Promise {<resolved>: undefined}
checkTime2: 1599639232855
checkTime1: 1599639234860

 

이 글을 공유합시다

facebook twitter kakaoTalk kakaostory naver band
loading