async/await이란?
- 프로미스 객체를 조금 더 간단하게 다룰 수 있는 문법
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.text())
.then((result) => { console.log(result); });
async function fetchAndPrint() {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const result = await response.text();
console.log(result);
}
fetchAndPrint();
- async: asynchronous의 줄임말로 비동기를 의미함
- 함수 안에 비동기적으로 실행될 부분이 있다는 것을 의미함
- 함수의 코드 중에서 프로미스 객체를 리턴하는 코드가 있다는 뜻
- await: 그 뒤에 코드를 실행하고 그 코드가 리턴하는 프로미스 객체가 fulfilled 상태, rejected 상태가 될 때까지 기다려 줌
- fulfilled 상태가 되고 그 작업 성공 결과를 리턴한 후에 그 다음 줄을 실행함
- await 키워드는 async 함수 안에서만 사용할 수 있음
async/await 구문의 실행 원리
async function fetchAndPrint() {
console.log(2);
const response = await fetch('https://jsonplaceholder.typicode.com/users');
console.log(7);
const result = await response.text();
console.log(result);
}
console.log(1);
fetchAndPrint();
console.log(3);
console.log(4);
console.log(5);
console.log(6);
동기 실행 코드처럼 생긴 비동기 실행 코드
function fetchAndPrint() {
console.log(2);
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => {
console.log(7);
return response.text();
})
.then((result) => { console.log(result); });
}
console.log(1);
fetchAndPrint();
console.log(3);
console.log(4);
console.log(5);
console.log(6);
- async/await 구문을 사용하면,
- Promise 객체를 사용할 때, then 메소드 등을 사용하지 않고도
- 마치 동기 실행 코드처럼 코드를 훨씬 더 간단하고 편하게 작성할 수 있으며 코드를 읽기에도 훨씬 편함
- async 함수 안의 코드가 실행되다가 await을 만나면, 일단 await 뒤의 코드가 실행되고, 코드의 실행 흐름이 async 함수 바깥으로 나가서 나머지 코드를 다 실행함
- 그 이후, await 뒤에 있던 Promise 객체가 fulfilled 상태가 되기를 기다립니다. 그리고 기다리던 Promise 객체가 fulfilled 상태가 되면 await이 Promise 객체의 작업 성공 결과를 리턴함
catch문과 finally문
async function fetchAndPrint() {
try {
const response = await fetch('https://www.google.www');
const result = await response.text();
console.log(result);
} catch(error) {
console.log(error);
} finally {
console.log('exit');
}
}
fetchAndPrint();
async 함수는 Promise 객체를 리턴합니다
- async가 붙은 함수는 항상 프로미스 개체를 리턴함
- 함수 안에서 무슨 값을 리턴한지에 따라서 결국 어떤 프로미스 객체가 리턴되는지가 달라짐
async 함수가 리턴하는 Promise 객체
- async 함수가 결국 Promise 객체를 리턴한다는 사실은 아주 중요함
- 왜냐하면 이 말은 곧 async 함수 안에서 다른 async 함수를 가져다가 쓸 수 있다는 뜻이기 때문임
1. 어떤 값을 리턴하는 경우
1) Promise 객체를 리턴하는 경우
- 해당 Promise 객체와 동일한 상태와 작업 성공 결과(또는 작업 실패 정보)를 가진 Promise 객체를 리턴함
- pending 상태의 Promise 객체, 이미 fulfilled 상태인 Promise 객체, 이미 rejected 상태인 Promise 객체를 리턴하는 경우 전부 다 해당됨
async function fetchAndPrint() {
return new Promise((resolve, reject)=> {
setTimeout(() => { resolve('abc'); }, 4000);
});
}
fetchAndPrint();
async function fetchAndPrint() {
return Promise.resolve('Success');
}
fetchAndPrint();
async function fetchAndPrint() {
return Promise.reject(new Error('Fail'));
}
fetchAndPrint();
2) Promise 객체 이외의 값을 리턴하는 경우
- Promise 객체 이외에 숫자나 문자열, 일반 객체 등을 리턴하는 경우에는, fulfilled 상태이면서, 리턴된 값을 작업 성공 결과로 가진 Promise 객체를 리턴함
async function fetchAndPrint() {
const member = {
name: 'Jerry',
email: 'jerry@codeitmall.kr',
department: 'sales',
};
return member;
}
fetchAndPrint();
2. 아무 값도 리턴하지 않는 경우
- fulfilled 상태이면서, undefined를 작업 성공 결과로 가진 Promise 객체가 리턴됨
async function fetchAndPrint() {
console.log('Hello Programming!');
}
fetchAndPrint();
3. async 함수 내부에서 에러가 발생했을 때
- rejected 상태이면서, 해당 에러 객체를 작업 실패 정보로 가진 Promise 객체가 리턴됨
async function fetchAndPrint() {
throw new Error('Fail');
}
fetchAndPrint();
async 함수 안의 async 함수
- async 함수 안에서는 또 다른 async 함수를 자유롭게 사용할 수 있음
- async 함수는 결국 프로미스 객체를 리턴하니까 그 앞에 await을 붙여서 쓰면 됨
async를 붙이는 위치
async function example1(a, b) {
return a + b;
}
const example2_1= async function add(a, b) {
return a + b;
};
const example2_2 = async function(a, b) {
return a + b;
};
const example3_1 = async (a, b) => {
return a + b;
};
const example3_2 = async (a, b) => a + b;
(async function print(sentence) {
console.log(sentence);
return sentence;
}('I love JavaScript!'));
(async function (a, b) {
return a + b;
}(1, 2));
(async (a, b) => {
return a + b;
})(1, 2);
(async (a, b) => a + b)(1, 2);
async 함수를 작성할 때 주의해야할 성능 문제(심화)
async function getResponses(urls) {
for(const url of urls){
const response = await fetch(url);
console.log(await response.text());
}
}
- 이전 URL에 리퀘스트를 보내고 리스폰스를 받아서 출력하고 나서야, 다음 URL에 대한 리퀘스트를 보낼 수 있다는 즉, 순차적인 작업 처리를 한다는 문제점이 있음
- 이전 URL에 대해서 await 문이 붙은 Promise 객체가 fulfilled 상태가 될 때까지는 그 다음 URL에 대한 작업들이 시작될 수 없기 때문임
- 순서는 상관없는 경우라면 이 코드는 성능 관점에서 아쉬운 점이 있는 코드임
async function fetchUrls(urls){
for(const url of urls){
(async () => {
const response = await fetch(url);
console.log(await response.text());
})();
}
}
- 위 코드에서 각 url에 리퀘스트를 보내고 리스폰스를 받는 코드를, 별도의 즉시실행되는 async 함수로 감싸줬음
- 각 URL에 대해서 fetch 함수를 실행해서 리퀘스트를 보내는 것을 순서대로 바로 실행해버림
- 모든 URL에 대한 리퀘스트를 쭉 보내버리고, 먼저 리스폰스가 오는 순서대로 그 내용이 출력됨
- 의도치 않게 순차 처리를 수행하는 비효율적인 코드를 짜지 않으려면 async 함수 안에서 무언가를 반복 처리하는 코드를 쓸 때 유의해야 함
- 순차적인 처리가 필요한 경우가 아니라면 방금 본 것처럼 각 작업을 다시 async 함수로 묶어주면 됨
두 가지 종류의 콜백(심화)
- 콜백: 함수의 파라미터로 전달되는 함수를 의미하는 넓은 의미의 개념
- 두 종류로 나뉨
- 동기 실행되는 콜백과
- 비동기 실행되는 콜백
- filter 메소드는 동기 실행되는 콜백임
- isOdd 콜백이 비동기 실행되는 콜백이었다면 그 뒤의
console.log(newArr);
가 먼저 실행되어야 함
const arr = [1, 2, 3, 4, 5, 6];
const newArr = arr.filter(function isOdd(num) {
return (num % 2 === 1);
});
console.log(newArr);
글 재미있게 봤습니다.