async와 await
async와 await
ko.javascript.info
async와 await는 Promise를 좀 더 편하게 사용할 수 있는 문법이다. javascript.info에 이해하기 쉬운 튜토리얼이 있어 정리해본다.
async 함수
async 키워드는 function 앞에 위치한다.
async function f() {
return 1;
}
function 앞에 async를 붙이면 해당 함수는 항상 Promise를 반환한다. Promise가 아닌 값을 반환하더라도 이행 상태의 Promise로 값을 감싸 이행된 Promise가 반환되도록 한다. 즉, async는 수식하는 function의 반환을 암시적으로 Promise로 변환한다.
async function f() {
return 1;
}
f().then(alert); // 1
위의 예시 코드를 실행하면 result가 1인 이행 Promise가 반환된다.
async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1
async function에서 명시적으로 Promise를 반환할 수도 있다. 위의 코드는 이전 예시와 같은 결과를 반환한다.
async가 붙은 함수는 반드시 Promise를 반환하고, Promise가 아닌 것은 Promise로 감싸 반환한다.
await
await 문법은 다음과 같다.
// await는 async 함수 안에서만 동작합니다.
let value = await promise;
자바스크립트는 await 키워드를 만나면 Promise가 처리될 때까지 기다린다. 결과는 그 후에 반환된다.
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000)
});
let result = await promise; // 프라미스가 이행될 때까지 기다림 (*)
alert(result); // "완료!"
}
f();
위의 코드는 1초 후 이행되는 Promise를 사용하는 예시이다.
함수를 호출하고, 함수 본문이 실행되는 도중에 (*)로 표시한 줄에서 실행이 잠시 중단되었다가 Promise가 처리되면 실행이 재개된다. 이 때 Promise 객체의 result 값이 변수 result에 할당된다. 따라서 위 예시를 실행하면 1초 뒤에 '완료!'가 출력된다.
await는 말 그대로 Promise가 처리될 때까지 함수 실행을 기다리게 만든다. Promise가 처리되면 그 결과와 함께 실행이 재개된다. Promise가 처리되길 기다리는 동안에는 엔진이 다른 일을 할 수 있기 때문에 CPU 리소스가 낭비되지 않는다.
await는 promise.then보다 좀 더 세련되게 promise의 result 값을 얻을 수 있도록 해주는 문법이다. promise.then보다 가동성이 좋고 쓰기도 쉽다.
예시
promise 체이닝을 async/await로 작성하는 예시를 보자.
function loadJson(url) {
return fetch(url)
.then(response => response.json());
}
function loadGithubUser(name) {
return fetch(`https://api.github.com/users/${name}`)
.then(response => response.json());
}
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);
setTimeout(() => {
img.remove();
resolve(githubUser);
}, 3000);
});
}
// 함수를 이용하여 다시 동일 작업 수행
loadJson('/article/promise-chaining/user.json')
.then(user => loadGithubUser(user.name))
.then(showAvatar)
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
promise 체이닝을 사용한 코드이다.
loadJson 함수를 먼저 실행한다. loadJson함수는 먼저 fetch(url)을 실행하고, 그 결과를 가지고 loadGithubUser 함수를 실행한다.
loadGithubUser함수는 입력받은 이름을 가지고 다시 fetch를 실행한다. 그리고 그 결과를 가지고 showAvatar함수를 실행한다.
마지막으로 showAvatar함수는 이미지를 생성하고, 3초 후에 다음 함수를 실행한다.(페이지에 알람을 띄운다)
- 먼저 .then 호출을 await로 바꾼다.
- function 앞에 async를 붙여 await를 사용할 수 있도록 한다.
먼저 실제로 함수를 호출하는 부분의 .then 호출을 await로 바꾼다.
function loadJson(url) {
return fetch(url)
.then(response => response.json());
}
function loadGithubUser(name) {
return fetch(`https://api.github.com/users/${name}`)
.then(response => response.json());
}
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);
setTimeout(() => {
img.remove();
resolve(githubUser);
}, 3000);
});
}
let user = await loadJson('/article/promise-chaining/user.json');
let githubUser = await loadGithubUser(user.name);
await showAvatar(githubUser);
alert(`Finished showing ${githubUser.name}`);
그리고 loadJson, loadGithubUser, showAvater 함수도 async/await로 변환한다. fetch는 자체적으로 promise 객체를 반환하므로 await로 변환할 수 있다.
async function loadJson(url) {
let response = await fetch(url);
return await response.json();
}
async function loadGithubUser(name) {
let response = await fetch(`https://api.github.com/users/${name}`);
return await response.json();
}
async function showAvatar(githubUser) {
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
await new Promise(resolve => setTimeout(resolve, 3000)); // 3초 대기
img.remove();
return githubUser;
}
let user = await loadJson('/article/promise-chaining/user.json');
let githubUser = await loadGithubUser(user.name);
await showAvatar(githubUser);
alert(`Finished showing ${githubUser.name}`);
이제 모든 함수를 풀어서 하나의 async 함수로 만든다.
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;
}
let githubUser = await showAvatar();
alert(`Finished showing ${githubUser.name}`);
요약
function 앞에 async 키워드를 추가하면 두 가지 효과가 있다.
- 함수는 언제나 promise를 반환한다.
- 함수 안에서 await를 사용할 수 있다.
promise 앞에 await 키워드를 붙이면 자바스크립트는 promise가 처리될 때까지 대기한다. 처리가 왼료되면 조건에 따라 아래와 같은 동작이 이어진다.
- 에러 발생 - 예외가 생성된다.
- 에러 미발생 - promise객체의 result값을 반환한다.
async/await 문법을 사용하면 읽고 쓰기 쉬운 비동기 코드를 작성할 수 있다.