비동기 작업은 동기 작업과 달리 순서가 보장되지 않는다. 하지만 비동기를 써야 하는데 순서대로 실행하는 것이 필요한 경우도 있을 수 있다. 예를 들어 네트워크에 요청을 보낸 후 받은 값으로 또 다른 요청을 한다고 하면 첫 번째 요청이 먼저 실행된 후에 두 번째 요청이 실행되어야 한다. 이런 상황을 자바스크립트에서는 콜백, 프로미스, async/await으로 다룰 수 있다.
cf 비동기 - 자바스크립트에서는 어떻게 비동기 작업을 처리할까?
function func1(func) {
console.log('1');
func();
}
function func2(func) {
setTimeout(function () {
console.log('2');
}, 0); // Asynchronous Callback
func();
}
function func3() {
console.log('3');
}
func1(func3);
// '1'
// '3'
// undefined
func2(func3);
// '3'
// undefined
// '2'
// setTimeout 안에 있는 콜백함수는 비동기적으로 처리된다.
// 따라서 func2의 실행이 다 끝나고 call stack이 빈 다음에야
// event loop에 의해 다시 call stack으로 돌아와 처리되기 때문에
// console창에 가장 나중에 찍힌 것이다.
then
을 체이닝하여 순차적으로 비동기 처리가 가능해졌다. ES7에서는 여기에 async
/await
이 추가되어 비동기 작업을 마치 동기인 것처럼 코드를 작성하여 Promise를 더욱 직관적으로 사용할 수 있게 되었다.new
키워드로 인스턴스화 하여 사용한다.resolve
, reject
함수를 받는다. new Promise((resolve ,reject) => {
if (err) {
reject(err)
} else {
resolve(result)
}
})
catch
를 붙여 발생할 수 있는 에러를 처리한다.async
키워드로 표시해야 한다. async 함수는 항상 Promise를 반환한다.await
은 async
표시 된 함수 안에서만 사용할 수 있다.await
키워드를 만나면 Promise가 resolve되거나 reject될 때까지 함수 실행이 멈춘다. await
은 주의해서 사용해야 하며 비동기 작업이 아주 많이 필요한 경우 Promise.all()
이 더 적당하다.Promise.all()
을 사용한다. 반드시 그래야 하는 것은 아니고 상황에 따라 적절하게 사용한다.async
키워드로 표시해야 한다. async 함수는 항상 Promise를 반환한다.await
은 async
표시 된 함수 안에서만 사용할 수 있다.await
키워드를 만나면 프로미스가 resolve되거나 reject될 때까지 함수 실행이 멈춘다. 첫 번째 요청이 완료되어야 두 번째 요청이 발생할 수 있다.
// 랜덤한 등장인물에 대한 정보를 요청할 URL 구하기
const randNum = Math.floor(Math.random() * 82) + 1;
const peopleURL = `https://swapi.dev/api/people/${randNum}`;
// 랜덤한 등장인물에 대한 정보 요청
const peopleReq = new XMLHttpRequest();
peopleReq.addEventListener("load", function () {
console.log("First request loaded");
const data = JSON.parse(this.responseText);
// 받은 등장인물의 이름 콘솔창에 출력
console.log("Character Name : " + data.name);
const filmURL = data.films[0];
// 받은 등장인물이 등장하는 영화들 중 하나에 대한 정보 요청
const filmReq = new XMLHttpRequest();
filmReq.addEventListener("load", function () {
console.log("Second request loaded");
const data = JSON.parse(this.responseText);
// 받은 영화의 제목 콘솔창에 출력
console.log("Appearing In : " + data.title);
});
// 영화 정보 요청 오류 처리
filmReq.onerror = function (err) {
console.log("Error : ", err);
};
// 영화 정보 요청 보내기
filmReq.open("GET", filmURL);
filmReq.send();
});
// 등장인물 정보 요청 오류 처리
peopleReq.onerror = function (err) {
console.log("Error : ", err);
};
// 등장인물 정보 요청 보내기
peopleReq.open("GET", peopleURL);
peopleReq.send();
// 랜덤한 등장인물에 대한 정보를 요청할 URL 구하기
const randNum = Math.floor(Math.random() * 82) + 1;
const peopleURL = `https://swapi.dev/api/people/${randNum}`;
// 랜덤한 등장인물에 대한 정보 요청
fetch(peopleURL)
.then((response) => response.json())
.then((data) => {
console.log("First request loaded");
// 받은 등장인물의 이름 콘솔창에 출력
console.log("Character Name : " + data.name);
const filmURL = data.films[0];
// 받은 등장인물이 등장하는 영화들 중 하나에 대한 정보 요청
return fetch(filmURL)
.then((response) => response.json())
.then((data) => {
console.log("Second request loaded");
// 받은 영화의 제목 콘솔창에 출력
console.log("Appearing In : " + data.title);
});
})
// 요청 오류 처리
.catch((err) => {
console.log("Error : ", err);
});
// 비동기 함수 getCharacterAndFilm 선언
async function getCharacterAndFilm() {
// 랜덤한 등장인물에 대한 정보를 요청할 URL 구하기
const randNum = Math.floor(Math.random() * 82) + 1;
const peopleURL = `https://swapi.dev/api/people/${randNum}`;
// 랜덤한 등장인물에 대한 정보 요청
const filmURL = await fetch(peopleURL)
.then((response) => response.json())
.then((data) => {
console.log("First request loaded");
// 받은 등장인물의 이름 콘솔창에 출력
console.log("Character Name : " + data.name);
return data.films[0];
})
// 요청 오류 처리
.catch((err) => {
console.log("Error : ", err);
});
// 받은 등장인물이 등장하는 영화들 중 하나에 대한 정보 요청
fetch(filmURL)
.then((response) => response.json())
.then((data) => {
console.log("Second request loaded");
// 받은 영화의 제목 콘솔창에 출력
console.log("Appearing In : " + data.title);
})
// 요청 오류 처리
.catch((err) => {
console.log("Error : ", err);
});
}
// getCharacterAndFilm 호출
getCharacterAndFilm();