[Javascript] async와 await, Promise 객체 등등의 개념 정리

2023. 10. 29. 17:00프로그래밍/JavaScript

- async와 awiat는 무엇인가?

async는 asynchronize의 줄임말으로써 비동기를 나타내는 말이다. synchronize는 동기화하다 라는 뜻이다. 컴퓨터 용어에서 동기와 비동기의 구분은 하나의 프로세스(스택)를 실행하면서 다른 프로세스를 실행가능하게 할 것인지 결정하는 요소이다.

그래서 async와 await의 개념에 대해서 알기 위해서는 비동기와 동기의 개념부터 이해해야한다.

이 부분은 내가 기존에 알고 있던 개념이라 생략한다. 

 

그렇다면 어떨때 async를 사용하고 await를 사용해야할까?

RESTful API를 호출할 때는 상식적으로 비동기로 처리해야한다. 왜냐하면 데이터를 요청했을 때 요청하는 동안의 프로세스가 그쪽으로 잡혀있으면 클라이언트는 요청 응답이 오기 전까지 아무것도 못하기 때문에 UX를 떨어뜨리는 요소가 된다. 

그래서 Java에서 AJAX통신은 비동기적으로 RESTful API를 호출하고 요청의 응답상태에 따라(success, error) 콜백함수를 통해 관련 로직을 처리한다. 

async와 await도 이와 비슷한 개념이다. 

async는 비동기 함수로 javascript에서 다음과 같이 나타낸다. 

async function test(){

	const fetchResponse = await fetch()...생략

}

 

async로 선언한 함수는 비동기로 실행하겠다는 뜻이다. 

async의 개념을 이해하기 가장 좋은 예제는 바로 시간차로 메서드의 호출(스택)이 종료되는 시점을 확인해보는 것이다. 아래 예제는 setTimeOut메서드를 이용해 시간차를 이용해 콜백함수로써 동작하는 함수와 flow에 따라 그냥 실행되는 함수 2개의 실행결과를 보여준다.

 

// 비동기 함수를 시뮬레이션한 Promise 객체 생성
function simulateAsyncOperation() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('비동기 작업 완료');
        }, 2000);
    });
}

// async 함수 정의
async function example() {
    console.log('비동기 작업 시작');
    const result = await simulateAsyncOperation(); // 비동기 작업의 완료를 기다림
    console.log(result); // 비동기 작업의 결과를 출력
    console.log('비동기 작업 완료');
}

// async 함수 호출
console.log('처리 시작');
example();
console.log('처리 종료');

 

처리 시작
비동기 작업 시작
처리 종료
비동기 작업 완료
비동기 작업 완료

이 함수를 실행하면 위와 같은 결과를 얻을 수 있다. 직접 실행해보면 이해가 더 빠를 것이다.

결론적으로 말하자면 async와 await키워드는 async함수 내부적으로 Promise객체를 사용할 때에는 비동기적으로 동작하지만 아닐 경우에는 일반 동기함수와 동일하게 동작한다. 그러나 await키워드를 쓰면 동기적으로 작동하는 것'처럼' 보이기는 한다. 그러나 실제로 내부적으로는 비동기적으로 데이터를 처리한다. 이 말이 의미를 자세히 알아야한다. 예를 들어 우리가 여러가지 데이터를 요청하기 위해 각기 다른 api를 2 3개 호출했다고 하자. 그 때 그 함수는 비동기 함수이라고 가정하고 함수내부적으로는 Promise객체를 반환하는 함수를 호출하고 있다. 이 때 await키워드를 사용하면 ajax의 콜백함수처럼 then()의 결과값이 return되기 전까지 아래의 코드를 실행하지 않는다. 그래서 이 부분이 동기적으로 보이는 것이다. 그러나 실제 api에 관련된 로직을 처리할 때에는 각기 다른 api에 대한 처리는 비동기적으로 이루어지고 결과값이 먼저 나온쪽부터 이후의 로직을 수행하게 될 것이다. 

 

처리종료 문자가 3번째로 실행되는 이유는 비동기 작업 시작에 해당하는 async function example이 await하고 있는 상태이기 때문이다. 

 

이까지 설명했음에도 아마 아직 풀리지 않는 의문점이 몇개가 있다. 그러나 우선은 개념적으로 이정도까지 공부하고 넘어가도록 한다.

그렇다면 Promise객체는 무엇일까? 

Promise 객체는 비동기 작업의 나중에 실행될 성공 혹은 실패에 따른 코드를 실행하고 값을 반환한다. 

Promise객체는 다음과 같은 형태로 사용한다.

 

function getPromise(){

    console.log('getPromise실행')

    return new Promise(resolve => {

        setTimeout(()=>{
            resolve();
            console.log('1초 지남.')
        },1000)
    })
}

async function promiseTest(){
    console.log('promiseTest실행')
    const result = await getPromise();
    console.log('종료ㄴㄴ')
}

promiseTest();
console.log('hi');

내가 이해한바로 정리하자면 바깥의 async키워드가 중요한 것이 아니고 내부적으로 어떤 로직을 수행하면서 Promise객체를 반환받을 것인지 등에 따라 함수에 async 키워드를 붙이는 것으로 사려된다.

 

Promise객체의 상태는 총 3가지가 있다.

1. pending(대기)

2. fullfill(이행)

3. reject(거부)

Promise객체는 특성상 우선 return new Promise()를 통해 Promise객체를 반환하는 함수를 호출할경우 결과값이 나오지 않더라도 호출한 부분에 값이 반환되게 된다. 단 이때 await키워드를 만나게되면 Promise객체는 위 세가지 상태중 '대기'상태로 놓이게 된다.

그 상태에서 Promise객체의 resolve() 함수를 통해 값이 반환되면 fullfill상태가 되고 네트워크 오류나 예외 발생 시에는 reject상태가 된다. await 키워드 아래의 코드는 resovle함수를 실행하지 않을시, 더 정확히 말하면 fullfill이나 reject의 결과값이 오지 않을 경우에는 계속 대기 상태로 놓이게 된다.