📖 Reference
📎 https://tlsdnjs12.tistory.com/15
📎 https://ko.javascript.info/async-await

동기와 비동기를 나누는 가장 큰 차이점은 어떤 실행 순서를 가지는 지에 있다.
자바스크립트는 기본적으로 비동기적(Asynchronous) 으로 동작한다.
JS에서도 아래와 같은 경우에 비동기적 처리가 발생한다.
👉 DOM Element의 이벤트 핸들러
→ 마우스, 키보드 입력 등, 페이지 로딩(DOMContentLoaded 등)
👉 타이머
→ setTimeout, 애니메이션 API(requestAnimationFrame)
👉 서버로 요청 및 응답
→ fetch API, AJAX(XHR)
즉, 콜백 함수(Callback Function)는 비동기 처리방식의 문제점을 해결해주기 위해 특점 시점에서 호출이 되도록 만드는 함수.
/* 예제 1. */
function A(callback){
console.log("A function");
callback();
}
function B(){
console.log("B function");
}
A(B);
// 결과 : A function B function
// -> Callback Function B를 A의 매개변수로 전달
/* 예제 2. */
function cal(cb, x, y){
return cb(x, y);
}
function add(a, b){
return a+b;
}
console.log(cal(add, 1, 2));
// 결과 : 3
“A promise is an object that may produce a single value some time in the future”
프로미스(Promise)란?
⇒ 자바스크립트 비동기 처리에 사용되는객체
❓ 비동기 처리란?
→ 비동기식 동작이 동기적으로 동작하도록 하는 처리.
🤔 Promise가 필요한 이유?
$.get('{url}/bbs/1', function(response){
...
});
상기의 API는 서버로부터 데이터를 응답받아오는 요청을 보낸다.
만약, 아직 서버로부터 응답을 받아오지 못했는데 데이터를 사용하고자 하면 오류가 발생한다.
⇒ 이를 해결하기 위한 방법 중 하나가 프로미스(Promise)
💡Promise의 기본 문법
const _promise = new Promise((resolve, reject) => {
// 비동기 작업
}
_promise
.then(() => {
console.log("this is then!");
})
.catch(() => {
console.log("this is catch!");
});
then : 해당 promise 가 성공했을 때의 동작을 지정한다.catch : 해당 promise 가 실패했을 때의 동작을 지정한다.
promise는약속이다.promise의 선언부는 ‘지금 넘겨줄 데이터가 없으니 추후 주겠다’라는 의미
💡Promise의 상태(State)

pending : 프로세스(promise)를 수행 중인 상태 (fulfill 혹은 reject 가 되기 전)fulfill(resolve) : 프로세스(promise)가 완료된 상태reject : 프로세스(promise)가 중단 혹은 실패한 상태settled : 프로세스(promise)가 성공이거나 실패가 되었든 일단 완료한 상태.// promise 선언부
const _promise = function(param){
return new Promise(function(resolve, reject){
// 비동기를 표현하기 위한 setTimeout func
window.setTimeout(function(){
if(param){
resolve("프로세스 완료");
}
else{
reject(Error("프로세스 실패"));
}
}, 3000);
});
}
// 실행부
_promise(true)
.then(function(data){
console.log(data); // promise 성공
}, function(error){
console.error(error); // promise 실패
});
위 코드를 해석해보자.
new Promise 로 promise 객체가 생성된 후, resolve 혹은 reject 가 호출되기 까지의 순간을 pending 상태라 할 수 있다.setTimeout 을 의미)이 끝난 뒤 실행부에서 인수로 넘긴 true가 resolve 를 호출하고, promise 작업이 실패했을 경우를 구현하기 위해 false 가 인수로 넘어가면 reject 가 호출된다._promise() 를 호출하면 Promise 객체가 반환된다.💡Promise의 then과 catch
/* 상황 1. then과 resolve */
const _promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("resolve되었습니다.");
}, 3000);
});
_promise
.then(value => console.log(value));
// then은 resolve된 값들을 받아오는 역할을 수행한다. (즉, promise(fulfill)가 완료되었을때)
/* 상황 2. catch와 reject */
// resolve가 아닌 reject된 값이 넘어온 경우 then을 사용할 시 에러가 발생한다.
// java의 try-catch와 같은 맥락으로 JS에서도 catch를 통해 reject로 넘어온 에러를 잡을 수 있다.
const _promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject("reject되었습니다.");
}, 3000);
});
_promise
.then(value => console.log(value))
.catch(error => console.log(error));
// 이때, then과 catch가 순차적으로 작성되었다고 해서 then이 수행된 후 catch가 실행되는게 아니다.
// 단순히 결과가 resolve라면 then으로, reject라면 catch로 넘어간다.
/* 상황 3. Chaining Promise와 catch */
// 만약, promise를 여러번 사용하고자 한다면?
// 예시 : promise를 통해 API로 data를 받아온 뒤, 받아온 데이터를 promise를 통해 복호화하는 경우
// 이와 같이, promise를 여러번, 순차적으로 사용하는 경우를 Chaining Promise라 한다.
const _promise = new Promise((resolve, reject) => {
resolve(2);
});
const plus = num => num + 1;
_promise
.then(plus)
.then(plus)
.then(plus)
.then(() => { throw Error("error") })
.then(result => console.log(result))
.catch(error => console.log(error));
// 위와 같이 Chaining Promise를 사용할 때 각각의 then에 모두 catch를 지정할 필요 없이
// 한 번의 catch로 모든 then을 해결할 수 있다.
// Promise 예외처리 문법 정리
try{
// 예외가 발생할 수 있는 명령 작성 (pending)
}catch(error){
// 에러가 발생하였을 때 (reject), error는 에러 객체를 뜻한다.
} finally {
// 예외가 발생하거나, 발생하지 않아도 무조건 실행시킬 명령
// 즉, try가 실행되든 catch가 실행되는 finally는 무조긴 실행된다.
}
비동기 프로그래밍에서 Callback function을 사용해야한다.
하지만, 콜백함수의 깊이가 깊어질수록 코드가 매우 복잡해지며 가독성이 떨어지는 이른바 콜백지옥 이 발생한다.

위와 같은 콜백지옥 을 해결하기 위해 ES6 에서 Promise 를 도입하였지만, promise가 resolve 되거나 reject 되는 경우를 대비하기 위해 체이닝 프로미스(Chaining promise) 를 작성한다.
아래의 코드를 보자.
function callbackHell(num){
const _promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = number + 10;
if(result > 50){
const err = new Error("너무 큰 숫자입니다.");
return reject(err);
}
resolve(result);
}, 1000);
})
return _promise;
}
// promise로 넘어가는 props이 50보다 작으면 resolve, 크면 reject
callbackHell(0)
.then((num) => { console.log(num); return callbackHell(num); }) // 10
.then((num) => { console.log(num); return callbackHell(num); }) // 20
.then((num) => { console.log(num); return callbackHell(num); }) // 30
.then((num) => { console.log(num); return callbackHell(num); }) // 40
.then((num) => { console.log(num); return callbackHell(num); }) // 50
.then((num) => { console.log(num); return callbackHell(num); }) // Error
.catch((err) => { console.log(err); });
위와 같이 코드가 직관적으로 들어오지않아 이해하기 힘들다.
이러한 체이닝 프로미스 를 사용할 때 발생되는 불편함을 해결하기위해 ES8 에서 async/await 를 도입하였고, 그 결과 비동기 처리를 직관적이며 깔끔하게 수행할 수 있게 되었다.
function callbackHell(num){
const _promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = number + 10;
if(result > 50){
const err = new Error("너무 큰 숫자입니다.");
return reject(err);
}
resolve(result);
}, 1000);
})
return _promise;
}
async function startTasks(){
try{
let result = await callbackHell(0);
console.log(result);
result = await callbackHell(result);
console.log(result);
result = await callbackHell(result);
console.log(result);
result = await callbackHell(result);
console.log(result);
result = await callbackHell(result);
console.log(result);
result = await callbackHell(result);
console.log(result);
result = await callbackHell(result);
}catch(err){
console.log(err)
}
}
async : async는 항상 function 앞에 위치한다. async가 수식된 함수는 항상 Promise 를 반환한다. 만약, Promise가 아닌 값을 반환하더라도 이행 상태 프라미스(resolved promise)로 값을 감싸 resolve된 promise를 반환한다.await : await는 반드시 async 함수 안에서만 동작한다. JS는 await를 만나게 되면 Promise가 처리될 때까지(resolve) 기다린다. 즉, JS의 비동기 방식 을 동기 로 편하게 처리해주는 역할을 수행한다.만약, promise 가 거부(reject)를 반환하면 어떻게 해야하나?
async function test(){
await Promise.reject(new Error("에러 발생"));
}
// 위 코드는 아래와 동일하다.
async function test(){
throw new Error("에러 발생");
}
위와 같이 promise가 거부되면 await는 에러를 반환한다.
await가 던진 에러는 try-catch 문을 사용하여 처리할 수 있다.
🤔 async와 await은 항상 옳을까?
async와 await 가 맞는 방법은 아니다.async 와 await 는 Promise를 사용하기 때문에 반드시 Promise 를 알아야한다. → 즉, async/await이 할 수 없는 동작을 단순히 promise로 해결할 수 있는 경우도 존재한다.⇒ 💡 결론은 시기적절하게 promise와 async/await를 사용하자.
fetch('api url')
.then(response => response.json())
.then(response => {
// 서버로부터 받아온 데이터를 처리하는 로직 작성
});
fetch 는 서버로 요청(Request) 를 보내고 응답(Response) 을 받을 수 있도록 해주는 메서드이다.XMLHttpRequest 와의 차이는? → fetch API 는 Promise 기반 메서드이기에 더욱 간편하게 사용할 수 있다는 차이가 있다.fetch를 호출하면 브라우저는 서버로 요청(Request)을 보낸 뒤 Promise 객체를 반환한다.Promise 객체는 resolve 되어 응답(Response) 객체가 반환된다.Promise 객체는 rejected 된다. 이 경우 catch 를 통해 예외처리할 수 있다. (네트워크 이슈 혹은 404 요청)fetch 로부터 받아오는 응답(Response) 객체는 서버에서 응답한 데이터에 대한 정보를 가지고 있다.ok , status 속성 등을 이용하여 응답 성공 여부를 확인할 수 있다.CORS, HTTP Origin header semantics 와 같은 개념들을 정의하고, 수정할 수 있다.const serverAPI = async (params) => {
try{
const res = await fetch("API URL");
const result = await res.JSON();
console.log("서버로부터 응답받은 데이터", result);
}catch(error){
console.log("에러발생", error);
}finally{
console.log("async 종료");
}
}
// fetch API 실행
serverAPI();