보고 와야 할 이전글: promise 정리
async와 await
async와 await라는 특별한 문법을 사용하면 promise를 좀 더 편하게 사용할 수 있습니다.
async 함수
async function f() {
return 1;
}
function 앞에 async를 붙이면 해당 함수는 항상 promise를 반환합니다.
async function f() {
return 1;
}
f().then(alert); // 1
명시적으로 프라미스를 반환하는 것도 가능한데, 결과는 동일합니다.
async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1
async가 붙은 함수는 반드시 프라미스를 반환하고, 프라미스가 아닌 것은 프라미스로 감싸 반환합니다.
await 함수
await 문법은 다음과 같습니다.
// await는 async 함수 안에서만 동작합니다.
let value = await promise;
await은 일반함수에서 사용할수없고, async 함수안에서만 동작합니다.
자바스크립트는 await 키워드를 만나면 프라미스가 처리될 때까지 기다립니다
결과는 그 이후 반환됩니다.
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(()=> resolve("완료"), 1000)
});
let result = await promise; //promise가 이행될때까지 기다림
alert(result); //"완료!"
}
f();
함수를 호출하고, 함수 본문이 실행되는 도중에 실행이 잠시 '중단’되었다가 프라미스가 처리되면 실행이 재개됩니다.
이때 프라미스 객체의 result 값이 변수 result에 할당됩니다. 따라서 위 예시를 실행하면 1초 뒤에 '완료!'가 출력됩니다.
await는 말 그대로 프라미스가 처리될 때까지 함수 실행을 기다리게 만듭니다.
promise가 처리되면 그 결과와 함께 실행이 재개되죠.
프라미스가 처리되길 기다리는 동안엔 엔진이 다른 일(다른 스크립트를 실행, 이벤트 처리 등)을 할 수 있기 때문에,
CPU 리소스가 낭비되지 않습니다.
await는 promise.then보다 좀 더 세련되게 프라미스의 result 값을 얻을 수 있도록 해주는 문법입니다.
promise.then보다 가독성 좋고 쓰기도 쉽습니다
📌일반 함수엔 await을 사용할 수 없습니다.
function f() {
let promise = Promise.resolve(1);
let result = await promise; // Syntax error
}
sync 함수가 아닌데 await 을 사용하면 문법 에러가 발생합니다.
function 앞에 async를 붙이지 않으면 이런 에러가 발생할 수 있습니다. 앞서 설명해 드린 바와 같이
await는 async 함수 안에서만 동작합니다.
promise와 async/await 비교하기
promise 예제
// 1 JSON 읽기
function loadJson(url) {
return fetch(url)
.then(response => response.json());
}
// 2 github 사용자 정보 읽기
function loadGithubUser(name) {
return fetch(`https://api.github.com/users/${name}`)
.then(response => response.json());
}
// 3 아바타 보여주기
function showAvatar(githubUser) {
return new Promise(function(resolve, reject) {
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
//3초대기
setTimeout(() => {
img.remove();
resolve(githubUser);
}, 3000);
});
}
// 함수를 이용하여 다시 동일 작업 수행
loadJson('/article/promise-chaining/user.json') //1
.then(user => loadGithubUser(user.name)) //2
.then(showAvatar)//3
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
// ...
async/await 예제
async function showAvatar() {
// JSON 읽기
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
// github 사용자 정보 읽기
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
let githubUser = await githubResponse.json();
// 아바타 보여주기
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// 3초 대기
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
showAvatar();
코드가 깔끔해지고 읽기도 쉬워졌습니다. 프라미스를 사용한 것보다 훨씬 낫네요.
에러 핸들링
프라미스가 정상적으로 이행되면 await promise는 프라미스 객체의 result에 저장된 값을 반환합니다.
반면 프라미스가 거부되면 마치 throw문을 작성한 것처럼 에러가 던져집니다.
async function f() {
await Promise.reject(new Error("에러 발생!"));
}
위 코드는 아래 코드와 동일합니다.
async function f() {
throw new Error("에러 발생!");
}
실제 상황에선 프라미스가 거부 되기 전에 약간의 시간이 지체되는 경우가 있습니다.
이런 경우엔 await가 에러를 던지기 전에 지연이 발생합니다.
await가 던진 에러는 throw가 던진 에러를 잡을 때처럼 try..catch를 사용해 잡을 수 있습니다.
async function f() {
try {
let response = await fetch('http://유효하지-않은-주소');
const jsonData = await response.json();
console.log(jsonData);
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
fetch 란?
fetch 매서드는 JavaScript에서 서버로 네트워크 요청을 보내고 응답을 받을 수 있도록 해주는 매서드이다.
XMLHttpRequest와 비슷하지만 fetch는 Promise를 기반으로 구성되어 있어서
더 간편하게 사용할 수 있다는 차이점이 있다.
try..catch가 없으면 아래 예시의 async 함수 f()를 호출해 만든 프라미스가 거부 상태가 됩니다. f()에 .catch를 추가하면 거부된 프라미스를 처리할 수 있습니다.
async function f() {
let response = await fetch('http://유효하지-않은-주소');
}
// f()는 거부 상태의 프라미스가 됩니다.
f().catch(alert); // TypeError: failed to fetch // (*)
📌 async/await 와 promise.then/catch
async/await을 사용하면 await가 대기를 처리해주기 때문에 .then이 거의 필요하지 않습니다.
여기에 더하여 .catch 대신 일반 try..catch를 사용할 수 있다는 장점도 생깁니다.
항상 그러한 것은 아니지만, promise.then을 사용하는 것보다 async/await를 사용하는 것이 대개는 더 편리합니다.
그런데 문법 제약 때문에 async함수 바깥의 최상위 레벨 코드에선 await를 사용할 수 없습니다.
그렇기 때문에 관행처럼 .then/catch를 추가해 최종 결과나 처리되지 못한 에러를 다룹니다.
위 예시의 (*)로 표시한 줄처럼 말이죠.
async/await 요약
function 앞에 async 키워드를 추가하면 두 가지 효과가 있습니다.
- 함수는 언제나 프라미스를 반환합니다.
- 함수 안에서 await를 사용할 수 있습니다.
프라미스 앞에 await 키워드를 붙이면 자바스크립트는 프라미스가 처리될 때까지 대기합니다.
처리가 완료되면 조건에 따라 아래와 같은 동작이 이어집니다.
- 에러 발생 – 예외가 생성됨(에러가 발생한 장소에서 throw error를 호출한 것과 동일함)
- 에러 미발생 – 프라미스 객체의 result 값을 반환
async/await를 함께 사용하면 읽고, 쓰기 쉬운 비동기 코드를 작성할 수 있습니다.
async/await를 사용하면 promise.then/catch가 거의 필요 없습니다.하지만 가끔 가장 바깥 스코프에서 비동기 처리가 필요할 때같이 promise.then/catch를 써야만 하는 경우가 생기기 때문에 async/await가 프라미스를 기반으로 한다는 사실을 알고 계셔야 합니다. 여러 작업이 있고, 이 작업들이 모두 완료될 때까지 기다리려면 Promise.all을 활용할 수 있다는 점도 알고 계시기 바랍니다.
출처 : https://ko.javascript.info/async-await
'STUDY > JavaScript' 카테고리의 다른 글
[JS] Web Speech API의 SpeechSynesisUtterance (0) | 2023.08.28 |
---|---|
[JS] 마이크로 입력 받는 오디오를 녹음하는 방법 (0) | 2023.08.11 |
[JS] 콜백함수 쉽게 배워보기 (0) | 2023.08.09 |
[JS] canvas api - 오각형 육각형 그리기 (0) | 2023.06.21 |
[JS] XMLHttpRequest return처리 (Callback) (0) | 2023.05.12 |