자바스크립트 async / await 이해하기 본문
반응형
async / await
1. async / await란?
async/await
는 JavaScript에서 비동기적인 동작을 처리하기 위한 문법입니다.- 이를 사용하면 비동기 코드를 동기적으로 작성하고 관리할 수 있습니다.
async
함수는 항상Promise
를 반환하며, 이것은 async 함수의 반환값이 Promise.resolve()에 전달된 다음 호출자에게 반환된 것과 같습니다.await
키워드는Promise
가 완료될 때까지 해당 코드의 실행을 일시 중지합니다.await
키워드는async
함수 내에서만 사용할 수 있습니다. 따라서,await
를 사용하려는 코드가async
함수 내에 위치하도록 주의해야 합니다.**async / await
** 구문을 사용하면 각 비동기 작업에서 다음 구문을 실행하기 전에 결과를 기다리며 차단되는 것처럼 보이는 함수를 작성할 수 있습니다.async / await
구문을 이용한 비동기 코드는 전통적인 동기적 코드에 버금가는 가독성을 가지고 있습니다.await
표현은 프라미스와 같은 비동기적인 표현뿐만 아니라, 어떠한 값으로도 작동합니다. 만약 프라미스가 아닌 값이 제공되면 그것의 동작은 Promise.resolve()에 전달된 값을 기다리는 것과 비슷합니다.
2. 사용 방법
async 함수 선언
- 비동기 동작을 처리할 함수를
async
키워드로 선언합니다. 이 함수는Promise
를 반환합니다.
async function fetchData() {
// 비동기 동작 처리
}
await 키워드 사용
await
키워드를 사용하여Promise
가 완료될 때까지 코드 실행을 일시 중지합니다.
async function fetchData() {
const data = await fetch(url);
// 데이터 요청이 완료될 때까지 대기하고, 데이터를 변수에 할당
console.log(data);
}
3. Async / await 에서의 에러처리
async / await
의 큰 장점 중 하나는try…catch
블록의 동작을 정규화하여 동기적throws
와 비동기적 프라미스의 거부 두 상황 모두에서 잘 작동하도록 하는 것입니다.- 에러처리는 간단하고, 가독성이 좋으며, 무엇보다도 비동기적 / 동기적 에러를 모두 지원해야 합니다.
function delayError(milliseconds) {
return new Promise((resolve, reject) => {
// milliseconds 후에 거절되는 Promise
setTimeout(() => {
reject(new Error(`Error after ${milliseconds}ms`));
}, milliseconds);
});
}
async function playingWithErrors(throwSyncError) {
try {
// 동기적 오류상황
if (throwSyncError) {
throw new Error("This is a synchronous error");
}
// 비동기적 오류상황
await delayError(1000);
} catch (err) {
// 동기적, 비동기적 오류상황에 맞게 메세지가 찍힌다.
console.error(`We have an error: ${err.message}`);
} finally {
console.log("Done");
}
}
// throws a synchronous error
playingWithErrors(true);
// awaited Promise will reject
playingWithErrors(false);
4. await와 return await의 차이
- 흔한 안티패턴 중 하나는 async / await와 함께 에러를 다룰 때 호출자에 거부하는 프라미스를 반환하고, 프라미스를 반환하는 함수의 로컬 try…catch 블록에서 에러가 잡히는 것을 예상하는 것입니다.
- 로컬
try…catch
블럭에서 에러를 잡고싶다면 await 키워드를 추가해야 합니다.
function delayError(milliseconds) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error(`Error after ${milliseconds}ms`));
}, milliseconds);
});
}
async function errorNotCaught() {
try {
// 거절되는 Promise 객체 return
// 이렇게되면 1초후에 거절되는 프라미즈 객체를 기다리지 않고 곧바로 return 하게 된다.
// 이렇게 되면 에러는 호출자에서 포착된다.
return delayError(1000);
// 만약 로컬에서 처리하고 프라미스의 거부를 포착하고 싶다면..
return await delayError(1000); // awiat 키워드 추가
} catch (err) {
console.error("Error caught by the async function: " + err.message);
}
}
errorNotCaught().catch((err) =>
console.error("Error caught by the caller: " + err.message)
);
// 사실상 위의 catch 구문에서 에러핸들링이 가능합니다.
5. 순차 실행을 위한 forEach와 async / await의 사용의 안티패턴
- 개발자들이 Array.forEach() 또는 Array.map()과 함께 async / await를 사용한 순차 비동기 반복을 구현하려고 시도하는 안티패턴이 존재합니다. 다음은 예상대로 동작하지 않습니다.
- await 키워드를 붙였지만 forEach는 기다려주지 않습니다.
const doSomethingAsync = (item) => {
return new Promise((res, rej) => {
setTimeout(() => {
res(item + 1);
}, (Math.random() + 1) * 1000);
});
};
const items = [1, 2, 3];
items.forEach(async (item) => {
const result = await doSomethingAsync(item);
console.log(result);
});
// 뒤죽박죽인 순서
// 원래라면 첫번째 아이템의 초가 많이 걸리더라도 기다려주고 그다음걸로 넘어가야하는데
// setTimemout을 포함한 프라미즈 구문을 기다리지 않음
- setTimeout의 초를 랜덤하게 준 상태에서 forEach의 콜백으로 async함수를 선언하고 await 구문으로 doSomethingAsync함수를 기다리는게 의도였지만 forEach 함수에 의해 doSomethingAsync가 반환하는 프라미스는 무시됩니다.
- 다음과 같이 for…of 나 Promise.all을 활용하여 해결할 수 있습니다.
for (const item of items) {
const result = await doSomethingAsync(item);
console.log(result);
}
// 또는
const results = await Promise.all(
items.map(async (item) => {
const result = await doSomethingAsync(item);
return result;
})
);
console.log(results);
안티패턴이란?
- 안티패턴은 소프트웨어 공학 분야 용어이며, 실제 많이 사용되는 패턴이지만 비효율적이거나 비생산적인 패턴을 의미한다.
반응형
6. 콜백 패턴 ⇒ async / await 구문으로 바꿔보기 예제
콜백지옥 예제
class UserInfo {
loginUser(id, password, onSuccess, onError) {
setTimeout(() => {
if (
(id === 'tom' && password === 'tom1234')
) {
onSuccess(id);
} else {
onError(new Error('no user matched'));
}
}, 2000);
}
getRoles(user, onSuccess, onError) {
setTimeout(() => {
if (user === 'tom') {
onSuccess({ name: 'tom', role: 'admin' });
} else {
onError(new Error('no roles'));
}
}, 1000);
}
};
const userInfo = new UserInfo();
const id = "tom"
const password = "tom1234";
userInfo.loginUser(
id,
password,
user => {
userInfo.getRoles(
user,
userRole => {
alert(`hello ${userRole.name}, you have a ${userRole.role} role`)
}, // onSuccess
error => {
console.log(error)
} // onError
);
}, // onSuccess
error => { console.log(error) } // onError
);
async / await 적용
class UserInfo {
// id, password를 입력받아서, 2초후에 로그인 id를 resolve 하는 프라미즈 객체 반환
loginUser(id, password) {
return new Promise((res, rej) => {
setTimeout(() => {
if (
(id === 'tom' && password === 'tom1234')
) {
res(id);
} else {
rej(new Error('no user matched'));
}
}, 2000);
});
}
// id를 전달받아서, 1초후에 userRole 객체를 resolve 하는 프라미즈 객체 반환
getRoles(user) {
return new Promise((res, rej) => {
setTimeout(() => {
if (user === 'tom') {
res({ name: 'tom', role: 'admin' });
} else {
rej(new Error('no roles'));
}
}, 1000);
});
}
};
// 기본 셋팅
const userInfo = new UserInfo();
const id = "tom"
const password = "tom1234";
// async 함수 선언
// 마치 동기코드처럼 위에서부터 순차대로 실행됩니다. (비동기 코드를 기다립니다.)
const process = async () => {
console.log("login start");
try {
const userId = await userInfo.loginUser(id, password);
const userRole = await userInfo.getRoles(userId);
console.log(`hello ${userRole.name}, you have a ${userRole.role} role`);
} catch (err) {
console.log(err);
} finally {
console.log("login complete");
}
console.log("end of the function")
}
process();
반응형
'JavaScript' 카테고리의 다른 글
JS function vs arrow function (4) | 2023.05.31 |
---|---|
Javascript - this 키워드 (0) | 2023.05.30 |
자바스크립트 Promise.allSettled() 이해하기 (0) | 2023.04.25 |
자바스크립트 Promise.all() 이해하기 (0) | 2023.04.25 |
자바스크립트 프라미스 (Promise) 이해하기 (0) | 2023.04.25 |
Comments