콜백의 특징? 익명 함수다 (매개변수로 들어가는 함수)
익명 함수의 익명 함수 또 익명 함수의 익명 함수...
가 반복되다 보면 들여쓰기가 너무 길어지고
이게 깊어지고, 또 깊어지고.... -> 해서 헬 수준이라 콜백 지옥이라고 한다.
콜백을 너무 많이 쓰면 유지보수를 하는데에는 굉장히 힘들다...
콜백을 너무 반복하다 보면 들여쓰기가 너무 깊어진다.
주로 이벤트 처리 및 서버 통신과 같은 비동기적인 작업 수행할 때 발생한다.
뭔가를 기다려야하고 동기적인 방법이 아닐 때 콜백지옥이 발생이 많이 한다.
문제는 가독성과 유지보수에서 정말정말 힘듭니다.
동기의 예시로 카페에서 주문한 후에 커피가 나올때까지 줄서서 기다려주세요!
비동기의 예시로 주문한 후에 진동벨이 울리면 커피 가지러 오세요!
동기는 한번에 하나의 작업만 수행하고, 순서대로 하나씩 작업을 수행한다.
비동기는 주문을 한꺼번에 다받고 먼저 처리된 것들을 빠르게 처리하는 수행을 한다.
동기 : synchronous
a. 현재 실행중인 코드가 끝나야 다음 코드를 실행하는 방식 *(동기적의 핵심)
b. cpu의 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적 코드
비동기 : a + synchronous => async
a. 실행중인 코드의 완료 여부와 무관하게 즉시 다음코드로 넘어가는 방식
b. setTimeoud, addEventListner
c. 별도의 요청,실행대기,보류 등과 관련된 코드는 모두 비동기 방식이다.
(서버의 통신이 관련된 코드는 모드 비동기적 방식이다.)
비동기적 코드의 이해!
setTimeout(function(){
console.log('여기가 먼저 실행될까?!?!?')
}, 1000);
console.log('여기좀 봐주세요!!!!');
위에서 동기적이라면 setTimeout
의 console
이 먼저 실행되야하지만
setTimeout
은 시간을 정하고 실행되는 비동기 함수이다.
따라서 맨 아래의 console
이 실행되고 setTimeout
의 1초가 지난후 console
이 실행된다.
동기적인 코드는 순서가 중요하고
비동기적인 코드는 먼저 끝난걸 먼저 끝내준다.
웹의 복잡도가 올라갈 수록 비동기적 코드의 비중이 늘어난다.
(그래서 콜백 지옥이 늘어나는 것이다.)
콜백지옥의 예시
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, '에스프레소');
이렇게 물고 물리면서 결국 끝까지 수행하는데
단점은 한번만 쓰고 말건데 이렇게 이름을 다 붙이면
인적 리소스가 낭비되는 방식이다.
우리가 더 이해하면 좋은 부분은
왜 중첩을 해야만 하는가?
비동기 작업 => 순서를 보장하지 않는다 X
그러니 언제 결과가 재여건이 나에게 다시 올지 모른다.
더 생각해보면 내부에서 setTimeout
을 입력해서 그렇지
실제 서버 클라이언트 외부 api
통신(날씨) 이 친구들은
순서를 보장할 수 없다!
서버통신 처럼 조금 복잡한 비동기 함수 로직으로 가게 되면
서버의 환경으로 시간이 꼬이거나하면 순서가 필요한 부분에 순서를 보장할 수 없다.
그렇기 때문에 비동기 함수의 일의 순서를 보장받기 위해 사용하다 보니
콜백지옥이 생기게 되는 것이다.
비동기 작업의 동기적 표현이 필요합니다.
promise, generator(es6), async.await(es7)
비동기 작업의 동기적 표현(1)
- promise(1)
promise
는 지금 완벽히 이해하기는 정말 어려운 일이다.
비동기 작업을 동기적으로 바꿀 수 있는게 promise
다
즉 비동기 처리에 대해 처리가 끝나면 알려달라는 약속입니다.
new 연산자
로 호출한 promise
의 인자로 넘어가는 콜백은 바로 실행된다.
그 내부의 resolve(성공) || reject(실패)
정보를 잘 얻어왔을 때 resolve
이제 서버에 문제가 있어 응답을 못줄경우 rejcect
실행되기 전까지 then(다음), catch(오류)
로 넘어가지 않는다.
비동기 작업이 완료될 때 resolve와 reject
를 호출한다.
new Promise(function(resolve){
setTimeout(function(){
var name = '에스프레소';
console.log(name);
resolve(name);
}, 500);
}).then(function(prevName){
// 이 안에서도 새롭게 Promise를 만들어요!
})
이렇게 then
을 사용했을 때 콜백함수의 인자에는
resolve
에 넣어놨던 이름(name)
이 들어가니
prevName
이전 이름으로 명명해줌
new Promise(function(resolve){
setTimeout(function(){
var name = '에스프레소';
console.log(name);
resolve(name);
}, 500);
})
.then(function(prevName){
// 이 안에서도 새롭게 Promise를 만들어요!
return new Promise(function(resolve){
var name = prevName + ', 아메리카노';
console.log(name);
resolve(name);
}, 500);
})
이런 형태로 끝에 then
을 붙이고 끝없이 사용 가능함.
그래서 리팩토링 과정이 필요해보임
var addCoffee = (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("아메리카노"));
이걸 이해하려면 addCoffee()()
형태인데
최초에 이름은 넣어주고 return
해야되는 함수가
맨 첫번째 부분이 아닌 2번째 부분을 리턴해야 setTimeout
이 작동된다.
그래서 초반에만 ()()
형태를 만들어주고, .then
으로 값을 추가만 했다.
마찬가지로 화살표 함수를 써도 전혀 문제가 없기 때문에 사용하고
삼항연산자는 굳이 한줄로 표현할 수 있기에 사용
마지막으로 백틱을 활용한 연산자는 es6문법
에서 가독성이 훨씬 뛰어나다.
iterable => 반복될 수 있는, 반복할 수 있는
제너레이터 문법은 반복할 수 있는 객체 생성
iterable 객체는 next 메서드를 순환 할 수 있는 구조로 되어 있다.
yield : 양보하다, 미루다
비동기 작업이 완료되는 시점마다 next 메서드
를 호출해주면
generator 함수 내부소스가 위 -> 아래 순차적으로 진행된다.
항상 왜 비동기 적인 요소를 동기적인 요소로 바꿔야하나
비동기적인 요소는 순서를 보장받을 수 없으니까,
순서를 보정받기 위한 로직에서 순서를 보정 받기 위해서
4-2 과제 해석
fetch(url) // fetch
는 비동기적인 작업
.then
은 그냥 25번째 줄을 사용하면 Promise {pending}
으로 데이터를 제대로 받아오지 못하는데
.then
을 활용해 제대로 데이터를 받아올때 까지 다음줄로 못넘어가게 만들어준다.
await then
의 역할은 데이터를 다 받아올 때 까지 기다리게 만들어주는 역할
여기서 .then
을 사용한 값에서 return
을 돌리는 이유는
.then 없이 fetch(url)
을 콘솔에 띄우면 Promise {pending}
이 출력된다.
이는 Promise
가 데이터를 다 가져오지 못하고 대기 상태를 의미한다.
즉 데이터가 항상 몇초에 다 받아오는지 정확하지 않기 때문에
비동기적인 함수로 실행해야 한다.
왜냐면 동기적인 함수는 항상 아주 빠르게 다음걸 읽고 다음걸 읽기 때문이다.
여기서 return response.json();
하는 이유는 then
도 함수로 사용되고 있지 않나?
then
안에 화살표 함수를 정의시킨 것과 같다.
따라서 return
이라는 의미는 내가 밖으로 빠져나가게 해주는 역할이다.
즉 response.json()
을 return
하면 result
에 담기는 것이다.
순서가 fetch 비동기적인 함수로 url
가져오는데
어? .then
이 데이터 다 받아올때까지 기다리게 한다
그러고 나서 받아온 값을 response에 담겨서 제어문으로 true false boolean
으로 처리하고,
return
을 하면 이 함수 내에서 빠져나간 변수 result
에 담을 수 있는 것이다.
그래서 지금 return
은 값을 상자안에서 빼내어주는 역할이라고 생각하면 좋다.