
콜백지옥이란
콜백 함수를 익명 함수(아래 이미지로 살짝 보시면…)로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 헬 수준인 경우
주로 이벤트 처리 및 서버 통신과 같은 비동기적 작업을 수행할 때 발생
뭐가문제일까? 가독성이 정말 지옥(hell) 오랜 상태로 이렇게 짜여왔기 때문에, 수정도 어렵다.
)](https://teamsparta.notion.site/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fa0fe6771-c192-466c-a168-5ca5a53185fd%2FUntitled.png?id=4566d6b7-8507-4afe-9009-d68ccfc9da17&table=block&spaceId=83c75a39-3aba-4ba4-a792-7aefe4b07895&width=2000&userId=&cache=v2)
(출처 : https://preiner.medium.com/callback지옥에-promise-적용하기-d02272ecbabe)
동기 vs 비동기
동기와 비동기의 개념

출처 : https://smallzoodevs-organization.gitbook.io/copy-of-javascript-study/day-05./1.
실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
setTimeout, addEventListner 등
별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드

(출처 : https://velog.io/@mrbartrns/til-16-asynchronous-of-js)
콜백지옥의 예시와 해결방안
조금은 익숙한 setTimeout을 통해 콜백 지옥의 간단한 예시!
// setTimeout 함수의 동작원리
setTimeout(function(){
// 기본적으로 1000ms이 지나야 여기 로직이 실행:)
console.log('hi');
}, 1000);
아래 예시코드는 다음과 같은 문제가 있음
setTimeout(
function (name) {
var coffeeList = name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
},
500,
"카페라떼"
);
},
500,
"카페모카"
);
},
500,
"아메리카노"
);
},
500,
"에스프레소"
);
첫 번째 해결방법은 [기명함수로 변환]하는 방법.
물고 물리면서 결국 끝까지 수행!
var coffeeList = '';
var addEspresso = function (name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(addAmericano, 500, '아메리카노');
};
var addAmericano = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addMocha, 500, '카페모카');
};
var addMocha = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addLatte, 500, '카페라떼');
};
var addLatte = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
};
setTimeout(addEspresso, 500, '에스프레소');
가독성 좋음 위에서 아래로 코드 흐름이 이어지니까. 근데, 한 번만 쓰고 말텐데, 이렇게 이름을 다 붙여야 하는건 좀 그렇지 않을까?
아쉽지만 위 코드는 근본적인 해결책은 아님. 이런 경우 때문에 자바스크립트에서는 비동기적인 작업을 동기적으로(동기적인 것 처럼 보이도록) 처리해주는 장치를 계속해서 마련해주고 있음. Promise, Generator(ES6), async/await(ES7)같은 것.
비동기 작업의 동기적 표현이 필요.
<비동기 작업의 동기적 표현(1) - Promise(1)>
💡 **Promise에 대해**사실, Promise를 지금 완벽히 이해하기는 정말 어려운 일.
Promise는 비동기 처리에 대해, 처리가 끝나면 알려달라는 ‘약속’
이 방법으로 비동기 -> 동기적 표현을 구현할 수 있음 🙂
new Promise(function (resolve) {
setTimeout(function () {
var name = '에스프레소';
console.log(name);
resolve(name);
}, 500);
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 아메리카노';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 카페모카';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 카페라떼';
console.log(name);
resolve(name);
}, 500);
});
});
<비동기 작업의 동기적 표현(2) - Promise(2)>
직전 예제의 반복부분을 함수화 한 코드 trigger를 걸어주기 위해 클로저 개념이 나왔지만, 여기서는 skip 하고, 다음 chapter에서 다루게 됨!
var addCoffee = function (name) {
return function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var newName = prevName ? (prevName + ', ' + name) : name;
console.log(newName);
resolve(newName);
}, 500);
});
};
};
**addCoffee('에스프레소')()**
.then(addCoffee('아메리카노'))
.then(addCoffee('카페모카'))
.then(addCoffee('카페라떼'));
<비동기 작업의 동기적 표현(3) - Generator>
💡 **이터러블 객체(Iterable)**제너레이터 문법이 등장해요. 생소하시죠?
*가 붙은 함수가 제너레이터 함수입니다. 제너레이터 함수는 실행하면, Iterator 객체가 반환(next()를 가지고 있음)
iterator 은 객체는 next 메서드로 순환 할 수 있는 객체. next 메서드 호출 시, Generator 함수 내부에서 가장 먼저 등장하는 yield에서 stop 이후 다시 next 메서드를 호출하면 멈췄던 부분 -> 그 다음의 yield까지 실행 후 stop
즉, 비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면 Generator 함수 내부소스가 위 -> 아래 순차적으로 진행 😊
var addCoffee = function (prevName, name) {
setTimeout(function () {
coffeeMaker.next(prevName ? prevName + ', ' + name : name);
}, 500);
};
var coffeeGenerator = function* () {
var espresso = yield addCoffee('', '에스프레소');
console.log(espresso);
var americano = yield addCoffee(espresso, '아메리카노');
console.log(americano);
var mocha = yield addCoffee(americano, '카페모카');
console.log(mocha);
var latte = yield addCoffee(mocha, '카페라떼');
console.log(latte);
};
var coffeeMaker = coffeeGenerator();
coffeeMaker.next();
<비동기 작업의 동기적 표현(4) - Promise + Async/await>
ES2017에서 새롭게 추가된 async/await 문을 이용했어요. 비동기 작업을 수행코자 하는 함수 앞에 async 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 붙여주면 됨.
Promise ~ then과 동일한 효과를 얻을 수 있음