[JavaScript] async / await

async / await 을 사용하면 프로미스(Promise)를 좀 더 편리하게 사용할 수 있다.

async / await 는 프로미스(Promise) 코드를 보다 간편하고 직관적으로 작성할 수 있게 해주는 API 이다.
 

async 함수

function 앞에 async 키워드를 작성하면 해당 함수는 프로미스(Promise)가 된다.

const asynchronous = async () => {
return "something";
};


기존의 프로미스 방식에서는 resolve 콜백함수와 reject 콜백함수를 인자로 받아오는 콜백함수를 선언했어야 했다.

하지만 async 키워드를 작성하면 프로미스(Promise)의 콜백함수를 작성하지않아도 해당 함수는 항상 프로미스를 반환하게 된다. 

만약 프로미스가 아닌 값을 반환하더라도 이행된 상태의 프로미스로 값을 감싸 이행된 프로미스를 반환해야한다.


const asynchronous = async () => {
return "something";
};

asynchronous().then(console.log); // something


위 예시 asynchronous 함수를 호출하면 아래의 사진과 같이 이행된 프로미스 "something"이 반환된다.




이 처럼 함수 앞에 async 키워드가 작성되면 이행된(fulfilled) 프로미스가 반환된다.

async 키워드를 사용하면 기존의 .then() 대신에 await을 사용할 수 있다.




await

await은 영어 단어 뜻 "기다리다" 와 동일하게 동작한다.즉 프로미스가 처리될 때까지 함수 실행을 기다리게 된다. 그리고 처리된 결과를 가지고 코드를 이어서 실행한다. 프로미스가 처리되는 동안에 다른 업무(다른 스크립트 실행, 이벤트 처리 등)을 할 수 있다.

await은 async 함수 안에서만 동작한다. async 키워드를 사용하지 않은 함수에서 await을 사용하면 문법 에러가 발생한다.

await은 Promise의 then과 같은 역할을 한다. 하지만 조금 더 사용하기 쉽고 가독성면에서 더 좋다.

자바스크립트가 await 키워드를 만나면 프로미스가 처리(settled)될 때까지 기다리다가 완료되면 결과값을 받아 반환한다.

const asynchronous = async () => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("비동기처리를 성공적으로 수행했다"), 2000);
});

let result = await promise; // 프라미스가 이행(fulfilled)될 때까지 기다린다.

console.log(result); // 2초후에 "비동기처리를 성공적으로 수행했다" 출력
};

asynchronous();

asynchronous 함수를 호출하고 코드를 순차적으로 실행하다가 await 키워드를 만나면 잠시 멈추어 프로미스 처리가 완료되면 코드가 이어서 실행된다.

위 코드의 경우 프로미스 객체의 결과값을 변수 result에 할당한다. 따라서 2초 뒤에 "비동기처리를 성공적으로 수행했다" 가 출력된다.



await은 최상위 레벨 코드에서 작동하지 않는다.

// 최상위 레벨 코드에선 문법 에러가 발생한다.
let response = await fetch('/article/promise/json');
let user = await response.json();

하지만 async 키워드가 적용된 익명함수로 코드를 감싸면 최상위 레벨 코드에서도 await 을 사용할 수 있다.

(async () => {
let response = await fetch('/article/promise/json');
let user = await response.json();
...
})();




에러 핸들링

Promise와 달리 async / await은 에러가 발생했을때 에러를 처리하기위해서는 try ~ catch 절을 사용해서 에러를 처리해야한다.

await가 에러를 던지면 try ~ catch 절을 사용해서 에러를 처리할 수 있다.

let asynchronous = async () => {
try {
let response1 = await fetch("http://응답실패 주소");
let response2 = await fetch("https://error.com");
} catch (error) {
console.log(error); // TypeError: failed to fetch
}
};

asynchronous();

각각의 비동기 처리마다 각각의 try ~ catch 절을 사용하지 않아도 되며 하나의 catch를 가지고 모든 에러를 처리할 수 있다.


try ~ catch 절을 사용하지 않은 경우

let asynchronous = async () => {
let response1 = await fetch("http://응답실패 주소");
let response2 = await fetch("https://error.com");
};

asynchronous().catch(console.log); // TypeError: Failed to fetch

try ~ catch 절을 사용하지 않으면 호출된 프로미스가 거부(rejected) 상태가 된다. 

하지만 따로 위 코드에서처럼 .catch()를 추가하면 거부(rejected)된 프로미스를 처리할 수 있다.




대부분의 경우 promise를 대신하여 async / await 을 사용해도 된다. 

하지만 상황에 따라 바깥 스코프에서 비동기 처리가 필요할 때 promise를 사용해야만 하는 경우가 발생하기도 한다.

마지막으로 여러개의 비동기 작업들이있고 해당 작업들이 모두 완료될 때까지 기다려야 한다면 Promise.all 을 사용해 처리할 수 있다.



댓글