Async & Await
: 비동기 함수를 간편하게 사용하도록 함
promise를 한 단계 감싸서 더 보기 편하게 만들어 주는 문법!
prominse chaining 의 복잡한 chaining 없이 마치 동기적인 코드를 작성하듯 훨씬 간편하게 쓸 수 있다
Async
: 비동기 함수를 정의할 때 사용하는 키워드. function 키워드 앞에 async를 붙여주면 비동기 함수로 변신한다
이때 async 키워드가 붙은 함수는 항상 promise를 반환하고, 함수 내에서 어떤 값을 리턴해주면 이 값은 promise로
감싸져서 반환된다 (우리 눈에는 보이지 않지만, 내부적으로 promise를 사용하는 것임)
getUser로부터 반환된 user는 promise가 된다
// Async
async function getUser() {
return '냥냥';
} // 항상 promise를 리턴하는 비동기 함수가 된다
const user = getUser();
console.log(user);
더이상 '냥냥'이라는 문자열이 아니라 promise 객체가 콘솔에 출력된 것을 확인할 수 있다
만약, '냥냥'이라는 문자열에 접근을 하려면 promise의 then을 사용하면 된다
// Async
async function getUser() {
return '냥냥';
}
const user = getUser();
user.then((name) => console.log(name));
그렇다면 async가 붙은 함수 안에 문자열 값이 아닌 promise를 직접적으로 리턴하면 어떻게 될까?
promise 객체를 직접적으로 반환하면 한번 더 promise로 감싸져서 반환되는 게 아니다 !
만약 async 키워드가 붙은 함수 안에서 문자열이나 숫자같은 값을 리턴하면 promise로 감싸져서 반환되지만,
직접적으로 promise를 리턴하게 되면 한번 더 promise로 감싸지는 게 아니라
promise는 그대로 반환되어서 user변수에 그대로 들어감
// Async
const promise = new Promise((resolve) => resolve('멍멍'));
async function getUser() {
return promise;
}
const user = getUser();
user.then((name) => console.log(name));
getUser가 비동기 작업을 수행하도록 만들어보자.
이 함수는 네트워크 요청을 해서 서버로부터 사용자 이름을 받아오는 일을 한다고 가정한다
networkRequest 함수는 네트워크 요청에 2초가 걸린다는 것을 흉내내는 함수임. 함수를 호출하고 2초 뒤 '데이터를 받아왔습니다.'가 출력될 것임.
// Async
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('데이터를 받아왔습니다');
resolve();
}, 2000);
});
}
async function getUser() {
networkRequest();
return '냥이';
}
const user = getUser();
user.then((name) => console.log(name));
우리가 원하는 것은 '데이터를 받아왔습니다'가 먼저 출력되는 것인데 반대로 '냥이'가 먼저 출력된다
왜 그런 것일까?
이유는 networkRequest가 끝나는 것을 기다려주지 않아서다
networkRequest는 비동기적으로 수행되기 때문에 끝날때까지 기다려주지 않고 바로 다음줄로 넘어가서 '냥이'가 리턴된다
getUser가 정상적으로 작동하기 위해서는 networkRequest가 끝날 때까지 잠시 멈춰서 기다렸다가 그 다음에 '냥이'를 리턴해주어야 한다. 이럴 때 사용하는 것이 바로 Await이다.
Await
networkRequest 앞에 await을 붙여주면 networkRequest가 끝날 때까지 함수의 진행을 잠시 멈춰서 기다렸다가 다 끝나고 나면 다음줄로 넘어가 '냥이'가 리턴되도록 만들 수 있다
// Async & Await
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('데이터를 받아왔습니다');
resolve();
}, 2000);
});
}
async function getUser() {
await networkRequest();
return '냥이';
}
const user = getUser();
user.then((name) => console.log(name));
이처럼 우리가 async 함수 내부에서 어떤 비동기 함수가 완료되기를 기다려야 한다면 그 앞에 await를 붙여주면 된다
await 키워드는 promise가 완료될 때까지 기다려주는 역할을 한다.
networkRequest는 2초 뒤에 완료되는 promise를 리턴하기 때문에 앞에 await을 붙여주면 promise가 끝날 때까지 잠시 멈춰 기다려주게 된다
만약 async 함수 안에서 여러개의 비동기 함수를 순차적으로 호출해주고 싶다면 await을 여러번 써서 각각의 비동기 함수가 완료될 때까지 기다려주면 된다
// Async & Await
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('데이터를 받아왔습니다');
resolve();
}, 2000);
});
}
async function getUser() {
await networkRequest(); // 2초 대기
await networkRequest(); // 2초 대기
return '냥이';
}
const user = getUser();
user.then((name) => console.log(name));
4초(2초 + 2초)를 기다렸다가 '냥이'가 출력되는 것을 볼 수 있다
이처럼 Async와 Await을 사용하면 비동기 작업을 마치 동기적으로 수행하는 것처럼 보이도록 코드 작성이 가능하다
코드가 한줄한줄 순서대로 수행되기 때문.
Promise chaining보다 Async&Await을 사용한 함수가 가독성이 훨씬 더 높다
// Async & Await
async function getUser() {
await networkRequest();
await networkRequest();
return '냥이';
}
// Promise chaining
function getUserPromise() {
return networkRequest()
.then(() => {
return networkRequest();
})
.then(() => {
return '냥이';
});
}
※ ※ 주의 !! ※ ※
Await 키워드는 반드시 Async 키워드가 붙은 함수 내부에서만 사용할 수 있다
만약 Async 함수 내부가 아닌 곳에서 Await 키워드를 사용하고 프로그램을 실행시키면 에러 발생
getData 함수 안에서 getUser와 getTodo를 순차적으로 호출해서 반환값인 '냥이'와 배열을 콘솔에 출력해보자.
// Async & Await
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 2000);
});
}
async function getUser() {
await networkRequest();
return '냥이';
}
async function getTodo() {
await networkRequest();
return ['청소하기', '밥먹기'];
}
async function getData() {
// await은 getUser, getTodo가 반환한 promise가 완료될 때까지 기다리는 역할
const user = await getUser();
console.log(user);
const toDo = await getTodo();
console.log(toDo);
}
getData();
2초 후에 '냥이', 2초 후에 배열이 호출된다
.
.
.
async function getData() {
const user = await getUser();
const toDo = await getTodo();
console.log(`${user}님 ${toDo}를 하세요`);
}
.
.
4초 뒤에 한번에 나올 수도 있다
에러 처리
일반 함수와 똑같이 try catch 문을 사용한다
위의 코드에서 getUser에 에러가 발생하도록 만들어보자
// Async & Await
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 2000);
});
}
async function getUser() {
throw new Error('에러가 발생했어요!');
await networkRequest();
return '냥이';
}
async function getTodo() {
await networkRequest();
return ['청소하기', '밥먹기'];
}
async function getData() {
const user = await getUser();
const toDo = await getTodo();
console.log(`${user}님 ${toDo}를 하세요`);
}
getData();
이 에러는 const user = await getUser();에서 발생한 것
이 부분을 try, catch로 감싸준다
// Async & Await
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 2000);
});
}
async function getUser() {
throw new Error('에러가 발생했어요!');
await networkRequest();
return '냥이';
}
async function getTodo() {
await networkRequest();
return ['청소하기', '밥먹기'];
}
async function getData() {
let user;
try {
user = await getUser();
} catch (error) {
console.log(error.message);
user = '익명';
}
const toDo = await getTodo();
console.log(`${user}님 ${toDo}를 하세요`);
}
getData();
try 블럭 안에서 에러 발생 시 에러가 잡혀서 catch 블럭으로 넘어간다
catch 블럭 안에서 user = '익명'으로 해주면 getUser에서 에러가 날 경우 user를 '익명'으로 만들어줄 수 있다
https://www.youtube.com/watch?v=6-8mbuUC3fk&list=PLZ5oZ2KmQEYiGLIDlsTQR_wdfCH9ZD6lx&index=10
'Front-end > JavaScript' 카테고리의 다른 글
[JavaScript - 비동기 시리즈 3] Promise (0) | 2025.03.23 |
---|---|
[JavaScript - 비동기 시리즈 2] 비동기 Callback (0) | 2025.03.23 |
[JavaScript - 비동기 시리즈 1] 동기 VS 비동기 (0) | 2025.03.22 |
[JavaScript] Callback 함수 (0) | 2025.03.22 |
[JavaScript] this와 bind (0) | 2025.03.19 |
댓글