브라우저에서 비동기 프로그래밍은 주로 통신과 같이 오래 걸리는 작업들을
브라우저에 위임할 때 이루어진다.
비동기 프로그래밍 방식은 프로그램의 성능과 응답성을 높이는 데 도움을 주지만,
코드가 실제로 실행되는 순서가 뒤죽박죽이라 코드의 가독성을 해치고 디버깅을 어렵게 만든다.
하지만! 단점을 보완하기 위해 만든 프로그래밍 기법이 생겼다.
웹페이지가 발전하면서 웹과 모바일에서는 사용자 인터렉션이 많아지게 되었다.
다양한 이벤트요소들이 생겨나고
여러 통신들이 일어나야하고,
다양한 애니메이션도 나타나게 해야했다.
하지만 이러한 것들로 인해 사용자는 자연스럽게 대기시간을 겪게 된다.
대기시간이 잦고 길어질수록 사용자들은 불편을 겪을 것이고,
결국엔 웹에서 이탈하게 되고 웹 이용률 하락까지 불러온다.
프론트엔드 영역에선 비동기 처리는 피할 수 없는 문제.
다른 함수의 인수로 넘기는 함수를 말한다. 그리고 그 콜백함수는 함수를 전달받은 함수안에서 호출된다.
특정 함수의 동작이 끝남과 동시에, 다른 여러 가지 함수를 호출해야 할 경우에 사용된다.
const test = (callback) => {
console.log("테스트함수 안에 있는거야!");
callback()
};
test(()=> {console.log("이게바로 콜백")});
//테스트 함수 안에 있는거야!
//이게바로 콜백
const coffee = (callback, delay) => {
callback();
};
console.log("주문 도와드리겠습니다.");
setTimeout(() => {
console.log("음료 나왔습니다!");
}, 0);
console.log("다음 고객님!");
// 주문 도와드리겠습니다!
// 다음 고객님!
// 음료 나왔습니다!
비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제이다.
그렇다면 왜 우리가 콜백 지옥에 빠지게 되는 걸까?
콜백 함수는 비동기인데 그 안에 동기적인 코드를 넣으려고 하다보니
자연스럽게 중첩함수가 많이 생기고,
자연스럽게 콜백지옥에 빠지게 되는 것이다.
delay 첫번째 함수안에 비동기를 위해서 중첩시키고 계속 중첩시키다보면
중첩 함수들이 엄청 늘어나면서 콜백지옥에 빠지게 된다.
const delay = (sec, callback) => {
setTimeout(() => {
callback(new Date().toISOString());
}, sec * 1000);
};
console.log("start", new Date().toISOString());
delay(1, (result) => {
console.log(1, result);
delay(1, (result) => {
console.log(2, result);
delay(1, (result) => {
console.log(3, result);
});
});
});
이런 콜백지옥을 해결하는 방법은 Promise와 Async 방법이 있다.
콜백지옥을 탈출을 사용하는 자바스크립트 문법
const promise = new Promise((resolve,reject)=> {
//네트워크 통신이나 파일 읽어오는 기능이 들어오는 것이 좋음
console.log("이번엔 프로미스다!")
setTimeout(()=>{
resolve('dohee!');
},2000);
});
const promise = new Promise((resolve,reject)=> {
//네트워크 통신이나 파일 읽어오는 기능이 들어오는 것이 좋음
console.log("이번엔 프로미스다!")
setTimeout(()=>{
reject(new Error('no network'));
},2000);
});
promise.then((value) =>{
console.log(value);
}).catch(error => {
console.log(error);
}).finally(()=> {
console.log("마지막에 떠야함");
})
const fetchString = new Promise((resolve, reject) => {
setTimeout(() => resolve("가"), 1000);
});
fetchString
.then((str) => str + "나")
.then((str) => str + "다")
.then((str) => str + "라")
.then((str) => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("바보"), 1000);
});
})
.then((str) => {
return console.log(str);
});
//바보
promise를 만드는 순간 우리가 전달한 executer 콜백함수가 바로 실행됨.
만약 우리가 네트워크를 통신하는 코드를 작성했다면 promise가 만들어지는 순간
네트워크 통신을 하게 됨.
간과하고 넘어가면 불필요한 네트워크 통신을 하게 된다.
Promise를 간결하고 좀 더 동기적으로 보여주게 하는 도구이다.
async와 await을 동기식으로 코드를 작성하는 것처럼
간편하게 코드를 짤 수 있게 도와준다.
함수는 언제나 Promise를 반환합니다.
함수 안에서 await를 사용할 수 있습니다.
async
는 항상 함수 앞에 붙는다.async
를 붙이면 해당 함수는 항상 Promise를 반환한다.async function promise() {
return "dohee!";
}
const P = promise();
P.then(console.log);
console.log(P);
//Promise{<pending>} 상태값
//dohee!
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function dog() {
await delay(1000);
return "🐶";
}
async function cat() {
await delay(1000);
return "🐱";
}
//promise 적용버전
function pickPets() {
return dog().then((dog) => {
return cat().then((cat) => `${dog}+${cat}`);
});
}
//async 적용버전
async function pickPets() {
const dogpick = await dog();
const catpick = await cat();
return `${dogpick}+${catpick}`;
}
//병렬처리 적용버전
async function pickPets() {
const dogPromise = dog();
const catPromise = cat();
const dogpick = await dogPromise;
const catpick = await catPromise;
return `${dogpick}+${catpick}`;
}
pickPets().then(console.log);