13 리팩토링 5 - async & await를 이용한 비동기 처리
source: categories/study/vue-beginner-lv3/vue-beginner-lv3_9-04.md
13.1 자바스크립트 비동기 처리 패턴의 발전 과정
// 개발자가 짜고싶은 비동기 코드의 모습 (현재 async, await를 이용하면 아래와 같은 형식으로 짤 수 있음)
var id = $.get('domain.com/id');
if (id === 'john') {
var products = $.get('domain.com/products');
}
// 하지만 비동기 코드 작성할 때의 현실 (과거)
$.get('domain.com/id', function (id) {
if (id === 'john') {
$.get('domain.com/products', function (products) {
console.log(products);
})
}
})
// promise then 메서드 체이닝
function getId() {
return new Promise(function (resolve, reject) {
$.get('domain.com/id', function (id) {
resolve(id);
})
})
}
getId()
.then(function (id) {
if (id === 'john') {
return new Promise(function (resolve, reject) {
$.get('domain.com/products', function (products) {
resolve(products)
})
})
}
return new Promise(function (resolve, reject) {
reject();
})
})
.then(function () {
})
.catch()
Promise then catch 메소드 체이닝 테스트
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
}, 2000)
})
}
asyncFunc()
.then((val) => {
console.log(val);
})
.catch(error => {
console.log(error)
})
// success
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
}, 2000)
})
}
asyncFunc()
.then(val => {
console.log(val);
if (false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('second success');
}, 2000)
})
}
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed');
}, 2000)
})
})
.then(val => {
console.log(val);
})
.catch(error => {
console.log(error)
})
Promise then catch 메소드 체이닝할 때 이렇게 작성하는게 그나마 깔끔할듯?
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
}, 2000)
})
}
asyncFunc()
.then(val => {
console.log(val);
return new Promise((resolve, reject) => {
if (true) {
setTimeout(() => {
resolve('second success');
}, 2000)
} else {
setTimeout(() => {
reject('failed');
}, 2000)
}
})
})
.then(val => {
console.log(val);
})
.catch(error => {
console.log(error)
})
Promise then catch 메소드 체이닝보다 더 깔끔한거 - async await
// 개발자가 짜고싶은 비동기 코드의 모습 (현재 async, await를 이용하면 아래와 같은 형식으로 짤 수 있음)
var id = $.get('domain.com/id');
if (id === 'john') {
var products = $.get('domain.com/products');
}
console.log(products);
13.2 async & await 문법 소개
async
, await
는 자바스크립트 비동기 처리 패턴의 최신 문법입니다.
Promise
와 Callback
에서 주는 단점들을 해결하고 자바스크립트의 비동기적 사고 방식에서 벗어나 동기적(절차적)으로 코드를 작성할 수 있게 도와줍니다.
기본 문법
async
함수의 기본 문법은 다음과 같습니다.
async function fetchData() {
await getUserList();
}
async
함수는 함수의 앞에 async
를 붙여주고 함수의 내부 로직 중 비동기 처리 로직 앞에(Promise 인스턴스 반환하는 함수여야함
) await
를 붙여주면 됩니다.
좀 더 정화갛게 말하면 Promise
객체를 반환하는 API 호출 함수 앞에 await
를 붙입니다.
기본 예제
async function fetchData() {
var list = await getUserList();
console.log(list);
}
function getUserList() {
return new Promise(function (resolve, reject) {
var userList = ['user1', 'user2', 'user3'];
resolve(userList);
})
}
fetchData()
함수에서 getUserList()
함수를 호출하고 나면 Promise
객체가 반환됩니다.
그리고 그 Promise
는 실행이 완료된 상태(resolve
)이며 실행의 결과로 userList
배열을 반환하고 있습니다.
따라서 fetchData()
를 호출하면 userList
의 배열이 출력됩니다.
13.3 async & await 예제 소개
<template>
<div>
<button @click="loginUser">
login
</button>
<h1>List</h1>
</div>
</template>
<script>
import axios from 'axios';
export default {
methods: {
loginUser() {
axios.get('https://jsonplaceholder.typicode.com/users/1')
.then(response => console.log(response))
.catch(error => console.log(error))
}
}
}
</script>
<template>
<div>
<button @click="loginUser">
login
</button>
<h1>List</h1>
<ul>
<li v-for="item in items">{{item}}</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
items: [],
}
},
methods: {
loginUser() {
axios.get('https://jsonplaceholder.typicode.com/users/1')
.then(response => {
// Promise chaining에 어긋나는 안티패턴이긴 하지만, 이런식으로 비동기 처리를 할 수 있다.
if (response.data.id === 1) {
console.log('사용자가 인증되었습니다.');
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(response => {
this.items = response.data;
})
.catch()
}
})
.catch(error => console.log(error))
}
}
}
</script>
13.4 async await 예제 실습
위 예제 코드에 async await 적용해보자
<template>
<div>
<button @click="loginUser1">
login
</button>
<h1>List</h1>
<ul>
<li v-for="item in items">{{item}}</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
items: [],
}
},
methods: {
loginUser() {
axios.get('https://jsonplaceholder.typicode.com/users/1')
.then(response => {
// Promise chaining에 어긋나는 안티패턴이긴 하지만, 이런식으로 비동기 처리를 할 수 있다.
if (response.data.id === 1) {
console.log('사용자가 인증되었습니다.');
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(response => {
this.items = response.data;
})
.catch()
}
})
.catch(error => console.log(error))
},
// async 함수를 사용하실 때 await 키워드도 꼭 같이 사용해야됩니다. 사용 안하면 의미가 없습니다.
// async만 작성하고 나중에 babel로 트랜스파일 해보시면 Promise로 비동기 처리하는게 의미가 없어집니다.
// 항상 비동기 처리 앞에 await를 붙여줘야지 async 함수가 제대로 동작을합니다.
async loginUser1() {
const response = await axios.get('https://jsonplaceholder.typicode.com/users/1');
if (response.data.id === 1) {
console.log('사용자가 인증되었습니다.');
const list = await axios.get('https://jsonplaceholder.typicode.com/todos');
this.items = list.data;
}
}
}
}
</script>
13.5 async await 에러 처리 방법과 공통화 함수 작성 방법
<template>
<div>
<button @click="loginUser1">
login
</button>
<h1>List</h1>
<ul>
<li v-for="item in items">{{item}}</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
import {handleException} from '../utils/hadler.js'
export default {
data() {
return {
items: [],
}
},
methods: {
loginUser() {
axios.get('https://jsonplaceholder.typicode.com/users/1')
.then(response => {
// Promise chaining에 어긋나는 안티패턴이긴 하지만, 이런식으로 비동기 처리를 할 수 있다.
if (response.data.id === 1) {
console.log('사용자가 인증되었습니다.');
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(response => {
this.items = response.data;
})
.catch()
}
})
.catch(error => console.log(error))
},
// async 함수를 사용하실 때 await 키워드도 꼭 같이 사용해야됩니다. 사용 안하면 의미가 없습니다.
// async만 작성하고 나중에 babel로 트랜스파일 해보시면 Promise로 비동기 처리하는게 의미가 없어집니다.
// 항상 비동기 처리 앞에 await를 붙여줘야지 async 함수가 제대로 동작을합니다.
async loginUser1() {
// Promise의 then, catch 처리는 네트워크 요청이라던지, 비동기 요청에 대해서만 예외처리를 합니다.
// 그런데 아래 try catch는 비동기 요청 뿐만아니라 일반적인 자바스크립트 코드 에러까지 같이 예외처리를 할 수 있기 때문에
// 훨씬 더 포괄적으로 넓게 에러 처리를 할 수 있습니다.
// try 안에 있는 구문들의 모든 에러를 catch에서 잡을 수 있습니다.
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users/1');
if (response.data.id === 1) {
console.log('사용자가 인증되었습니다.');
const list = await axios.get('https://jsonplaceholder.typicode.com/todos');
this.items = list.data;
}
} catch (error) {
handleException(error); // 이렇게 에러처리 공통화 함수를 사용할 수도 있다. 아래 예시함수이다.
console.log(error);
}
}
}
}
</script>
export function handleException(status) {
// ...
}
13.6 async 함수를 이용한 코드 리팩토링
const actions = {
async FETCH_LIST({commit}, pageName) {
try {
const {data} = await fetchList(pageName);
commit('SET_LIST', data);
} catch (error) {
console.log(error)
}
}
// 프로미스 then 메소드 체이닝을 제대로 이어가려면 프로미스 인스턴스를 return해야된다.
// FETCH_LIST({commit}, pageName) {
// return fetchList(pageName)
// .then(({data}) => {
// console.log(1);
// commit('SET_LIST', data);
// })
// .catch(error => console.log(error))
// }
}
async
함수는 어떤걸 return
하던간에 Promise
객체로 return
합니다.
Note
다시 현재 스피너 및 페이지 전환효과(트랜지션) 작동원리 순서
/news
라우터로 진입/news
로 가기 전에 네비게이션 가드beforeEnter
로직 실행- Event bus로 스피너 실행
- dispatch로
state
의actions
에 있는 비동기 데이터 요청FETCH_LIST
함수 실행FETCH_LIST
함수는 받아온 data를state
의beforeList
에 담음beforeList
는 각 컴포넌트에 바인딩된state
가 아님- 그래서 페이지 컴포넌트가 재렌더링안됨
- Event bus로 스피너 종료
- next() 함수 실행
next()
실행되고난 후/news
페이지로 이동중에 페치지 전환효과transition
발생. 현재 페이지가 페이드아웃함/news
페이지로 진입하면서created
라이프사이클 훅 실행state
의list
에 업데이트된beforeList
값이 담김/news
에 새로운list
값으로 렌더링이 되면서 스르륵 등장함
13.7 [실습 안내] async await 실습 안내
Promise
, then
, catch
를 async
, await
로 전부 수정해보십시오.