🌀
비동기
중급 · 선수: 15_Promise
16. async / await
`async` 키워드가 붙은 함수는 반환값이 자동으로 Promise 로 감쌉니다.
asyncawait비동기에러 처리
소요 시간
⏱ 약 1.5시간
난이도
📊 중급
선수 조건
🎯 단원 15
결과물
`async` 키워드가 붙은 함수는 반환값이 자동으로 Promise 로 감쌉니다.
이 강의에서 배우는 것
- 1`async` 함수가 항상 Promise 를 반환함을 안다.
- 2`await` 로 Promise 의 값을 동기처럼 받아낼 수 있다.
- 3`try/catch` 로 비동기 에러를 처리한다.
- 4병렬 실행과 순차 실행의 차이를 안다.
- 5`Promise.all` 과 `await` 을 조합하여 효율적으로 처리한다.
소개
`async` 키워드가 붙은 함수는 반환값이 자동으로 Promise 로 감쌉니다.
핵심 개념
1. async 함수
`async` 키워드가 붙은 함수는 반환값이 자동으로 Promise 로 감쌉니다.
javascript
async function f() {
return 42;
}
f().then((v) => console.log(v)); // 422. await
`await` 는 Promise 가 결정될 때까지 함수 실행을 일시정지합니다. `async` 함수 내부에서만 쓸 수 있습니다(최상위 모듈 제외).
javascript
async function main() {
const value = await Promise.resolve(10);
console.log(value); // 10
}3. try / catch
비동기 에러는 try/catch 로 잡습니다.
javascript
async function load() {
try {
const data = await fetchData();
console.log(data);
} catch (err) {
console.error(err);
}
}4. 병렬 vs 순차
연속된 `await` 은 앞 작업이 끝나야 다음이 시작되므로 느립니다. 독립적인 작업은 `Promise.all` 로 병렬화하세요.
javascript
// 느림 (순차)
const a = await fetchA();
const b = await fetchB();
// 빠름 (병렬)
const [a, b] = await Promise.all([fetchA(), fetchB()]);핵심 예제
| 파일 | 다루는 내용 |
|---|---|
| 01_async_basic.js | async 함수와 await 기본 |
| 02_try_catch.js | try/catch 로 에러 처리 |
| 03_promise_all.js | await + Promise.all |
| 04_parallel.js | 병렬 vs 순차 시간 비교 |
src/01_async_basic.js
javascript
/**
* async 함수와 await 의 기본 동작.
*/
function delay(ms, value) {
return new Promise((resolve) => setTimeout(() => resolve(value), ms));
}
async function main() {
console.log("시작");
const value = await delay(200, "안녕!");
console.log("받음:", value);
console.log("끝");
}
main();
src/02_try_catch.js
javascript
/**
* try/catch 로 비동기 에러를 처리합니다.
*/
function fetchData(ok) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (ok) resolve({ id: 1 });
else reject(new Error("네트워크 실패"));
}, 100);
});
}
async function run() {
try {
const data = await fetchData(false);
console.log("성공:", data);
} catch (err) {
console.error("에러:", err.message);
} finally {
console.log("정리");
}
}
run();
src/03_promise_all.js
javascript
/**
* await 과 Promise.all 을 조합해 병렬 결과를 받습니다.
*/
function fetchValue(label, ms) {
return new Promise((resolve) => setTimeout(() => resolve(label), ms));
}
async function loadAll() {
const [a, b, c] = await Promise.all([
fetchValue("A", 100),
fetchValue("B", 200),
fetchValue("C", 150),
]);
console.log("모든 값:", a, b, c);
}
loadAll();
src/04_parallel.js
javascript
/**
* 순차 await 와 병렬 await(Promise.all)의 실행 시간을 비교합니다.
*/
function work(label, ms) {
return new Promise((resolve) => setTimeout(() => resolve(label), ms));
}
async function sequential() {
const start = Date.now();
await work("A", 200);
await work("B", 200);
await work("C", 200);
console.log("순차:", Date.now() - start, "ms");
}
async function parallel() {
const start = Date.now();
await Promise.all([work("A", 200), work("B", 200), work("C", 200)]);
console.log("병렬:", Date.now() - start, "ms");
}
(async () => {
await sequential();
await parallel();
})();
자주 하는 실수
- `async` 가 아닌 함수에서 `await` 사용 → SyntaxError.
- 독립적인 작업을 연속 `await` 으로 작성하여 시간 낭비.
- `try/catch` 없이 await 하여 unhandled rejection.
- `forEach((x) => await ...)` 는 동작하지 않음(콜백이 async 가 아님).
- async 함수의 반환값을 then 으로 풀지 않고 그대로 사용.
FAQ
Q1. await 을 모듈 최상위에서 사용할 수 있나요?
ES2022 의 top-level await(ESM 한정)에서 가능합니다.
Q2. async 함수가 throw 하면?
반환된 Promise 가 rejected 상태가 됩니다.
Q3. await 와 .then 중 무엇이 좋은가요?
가독성은 보통 await 가 좋습니다. 분기/조합이 단순할 땐 .then 도 충분합니다.
과제
- async 함수로 사용자와 게시글을 차례대로 가져오기.
- 여러 URL 을 Promise.all 로 병렬 호출 시뮬레이션.
homework/README.md
## 1. 사용자 + 게시글 가짜 `fetchUser(id)` 와 `fetchPosts(userId)` 를 작성하고, async 함수에서 차례로 호출해 결과를 출력하세요.
## 2. 병렬 요청 3개의 URL 에 해당하는 가짜 요청을 Promise.all 로 동시에 시작하고 시간을 측정하세요.
답안은 `answer/` 디렉터리를 참고하세요.
homework/answer/homework_01.js
javascript
/**
* 사용자를 가져온 뒤 그 사용자의 게시글을 가져옵니다.
*/
function fetchUser(id) {
return new Promise((resolve) => {
setTimeout(() => resolve({ id, name: "지민" }), 100);
});
}
function fetchPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve([{ id: 1, userId, title: "첫 글" }]), 100);
});
}
async function main() {
try {
const user = await fetchUser(1);
const posts = await fetchPosts(user.id);
console.log(user.name, "의 게시글:", posts);
} catch (err) {
console.error(err);
}
}
main();
homework/answer/homework_02.js
javascript
/**
* 세 URL 의 가짜 요청을 병렬로 보내고 총 시간을 측정합니다.
*/
function fakeFetch(url, ms) {
return new Promise((resolve) => setTimeout(() => resolve(`${url} OK`), ms));
}
async function main() {
const start = Date.now();
const results = await Promise.all([
fakeFetch("/a", 200),
fakeFetch("/b", 250),
fakeFetch("/c", 150),
]);
console.log("결과:", results);
console.log("총 시간:", Date.now() - start, "ms");
}
main();
다음 단원
[17_DOM_선택과_조회](../../05_DOM/17_DOM_선택과_조회/) — 브라우저에서 HTML 요소 다루기