Asynchronous Programming In Javascript - Async & Await
1] Async Fuctions
1) 정의
Async & Await은 비동기 함수를 동기 함수처럼 코드를 짜게 해주는 문법(통사적 특징, syntactic feature)이라고 할 수 있다.
2) Async
Async를 함수에 쓰게 되면 비동기 함수가 된다.
비동기 함수는 event loop에서 비동기적으로 동작하는 함수이다.
Example
async function f() {
// code
}
3) Async의 리턴
Async function은 항상 Promise를 리턴한다.
만약 함수가 Promise를 리턴하지 않고 value만 리턴하면, Javascript는 자동적으로 Promise로 래핑한다.
따라서 값일 경우에는 같은 값을 가진 fulfilled Promise를 리턴한다.
이미 Promise를 리턴하고 있으면, 변화(modification) 없이 그대로 리턴한다.
3초 뒤에 fulfilled되는 Promise를 리턴할 경우, 3초 전에 pending이었다가 3초 후에 fulfilled가 되는 모습을 볼 수 있다.
2] Await Keyword
await은 일반 function에 사용할 수 없고, async function에만 사용 가능하다.
await은 Promise가 resolved되거나 rejected가 되는 것에 상관 없이 settled 될 때까지 기다릴 때 사용한다.
중요한 점은 Promise를 기다리므로, Promise는 pending 상태가 아니게 된다는 것이다.
Example
function getOne() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 3000);
});
}
async function f() {
const num = await getOne();
console.log(num);
}
f();
이렇게 코드를 짤 경우 3초 뒤에 1이 나오게 된다.
따라서 f()는 getOne()에서 Promise가 resolved되거나 rejected 될 때까지 기다린다는 것을 알 수 있다.
Promise가 resolved가 되면 값을 num 변수에다가 할당하게 된다.
그러나 이것은 전체 어플리케이션이 이 함수가 resolved 될 때까지 멈춘다는 것은 아니다.
Javascript engine은 이 함수가 멈춘 동안에 다른 함수나 이벤트를 처리할 수 있다.
Async Await을 사용하여, 기존의 Promise를 사용하는 함수를 쉽게 대체할 수 있다.
Example) 기존 Promise처럼 사용하려면 다음과 같이 사용 가능하다.
function f() {
getOne()
.then(num => console.log(num));
}
3] Async Await의 사용
Promise를 사용한 코드와 비교해 보았을 때, 기본적으로 then을 사용한 모든 부분에 await을 써주면 된다.
Example) Promise 사용
function getFetch() {
fetch('https://www.~~~') // url
.then(response => response.json())
.then(value => console.log(value.message));
}
Example) Async Await 사용
async function getFetch() {
let response = await fetch('https://www.~~~'); // url
let value = await response.json();
console.log(value.message));
}
4] Top Level Await
브라우저가 아닌 Node.js에서, ECMAscript module이 아닐 경우 await은 top level에 적용할 수 없다.
Example) 브라우저일 때
Example) Node.js일 때(ECMAscript module이 아닐 때)
package.json에 "type": "module"을 사용하거나, mjs파일로 만들면 실행 가능하다.
5] Handling Errors
1) 전통적인 try catch 사용
Async Await은 try catch를 사용할 수 있다.
2) then 또는 catch 사용
async function은 항상 Promise를 리턴하므로, then이나 catch를 사용할 수 있다.
Further Details
handle function을 통한 최신 비동기 로직 코드
// handle 함수 생성
const handle = async (promise) -> {
try {
const data = await promise;
return [data, undefined];
} catch (error) {
return [undefined, error];
}
}
// 사용
const [response, error] = await handle(비동기 함수);
if (error) {
alert("Error occurred!");
return;
}
// 추후 response 사용
6] Sequential and Parallel Execution
1) 순차적(Sequential) 실행
Async Await을 그냥 사용하게 되면, 순차적으로 실행이 된다.
Example
function getOne() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('one');
resolve(1);
}, 3000);
});
}
function getTwo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('two');
resolve(2);
}, 3000);
});
}
async function f() {
const one = await getOne();
const two = await getTwo();
console.log(one, two);
}
f();
3초후 1이 나오고, 2초 후 2가 나오게 된다.
2) 병렬적(Parallel) Execution
함수가 아닌 response에 await을 사용하면 된다.
Promise.all과 같다.
Example
function getOne() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('one');
resolve(1);
}, 3000);
});
}
function getTwo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('two');
resolve(2);
}, 3000);
});
}
async function f() {
const response1 = getOne();
const response2 = getTwo();
const one = await response1;
const two = await response2;
console.log(one, two);
}
f();
3초후 1과 2가 같이 나오게 된다.
Further Details
f()를 리팩토링하면 다음과 같이 사용할 수도 있다.
Example) response를 한 번에 할당
async function f() {
const response1 = getOne();
const response2 = getTwo();
const [one, two] = [await response1, await response2];
console.log(one, two);
}
Example) console.log에 직접 입력
async function f() {
const response1 = getOne();
const response2 = getTwo();
console.log(await response1, await response2);
}