TIL 240821 (콜백 함수 2)
2. 콜백 지옥과 비동기 제어
2 - 1 콜백지옥
콜백함수를 익명 함수로 전달하는 과정이 계속 반복되는 경우를 말한다.
주로 통신과 비동기적 작업을 할때 발생한다.
콜백지옥은 가독성이 안좋아서 수정하기도 어렵다.
2 - 2 동기와 비동기
동기 : 실행중인 코드가 완료되기까지 기다렸다가 다음 코드로 넘어가는것
비동기 : 실행중인 코드가 완료되지 않아도 실행된 후에 바로 다음 코드로 넘어가는것
통신이 들어간 코드는 보통 비동기적 코드이다.
비동기적 코드 예시
setTimeout(function(){
console.log('1')
}, 1000);
console.log('2');
이 코드는 비동기적 코드로 2 먼저 출력되고 1이 출력된다. (기다리지 않는다)
2 - 3 콜백지옥 해결방안
예시)
값 전달순서 : 아래 ㅡ> 위
setTimeout(
function (name) {
var icecreamList = name;
console.log(icecreamList);
setTimeout(
function (name) {
icecreamList += ", " + name;
console.log(icecreamList);
setTimeout(
function (name) {
icecreamList += ", " + name;
console.log(icecreamList);
setTimeout(
function (name) {
icecreamList += ", " + name;
console.log(icecreamList);
},
500,
"메로나"
);
},
500,
"바밤바"
);
},
500,
"누가바"
);
},
500,
"죠스바"
);
해결방안 )
기명함수로 변환
let coffeeList = '';
let addice1 = function (name) {
icecreamList = name;
console.log(icecreamList);
setTimeout(addice2, 500, '메로나');
};
let addice2 = function (name) {
icecreamList += ', ' + name;
console.log(icecreamList);
setTimeout(addice3, 500, '바밤바');
};
let addice3 = function (name) {
icecreamList += ', ' + name;
console.log(icecreamList);
setTimeout(addice4, 500, '누가바');
};
let addice4 = function (name) {
icecreamList += ', ' + name;
console.log(icecreamList);
};
setTimeout(addice1, 500, '죠스바');
가독성은 좋은데 이름을 하나하나 붙이는게 불편하다. 그래서 이 코드는 완전한 해결방안이 아니다.
ES6문법인 Promise, Generator와 ES7문법인 async/await 같은 것들로 비동기 작업의 동기적 표현이 필요하다.
2 - 4 비동기 작업의 동기적 표현
비동기적 코드는 순서를 보장하지 않는다.
Promise : 비동기 처리 중 처리가 끝나면 알려준다.( 성공 : resolve, reject = 실패)
- new로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행돼요.
- resolve 또는 reject 함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 다음(then), 오류(catch)로 가지 않는다.
Promise (1)
new Promise(function(resolve){
setTimeout(function(){
let name = "죠스바";
console.log(name);
resolve(name);
}, 500);
}).then(function(prevName){
// 여기도 새 Promise를 만든다
return new Promise(function(resolve){
setTimeout(function(){
let name = prevName + ", 메로나";
console.log(name);
resolve(name);
}, 500);
});
}).then(function(prevName){
// 여기도 새 Promise를 만든다
return new Promise(function(resolve){
setTimeout(function(){
let name = prevName + ", 바밤바";
console.log(name);
resolve(name);
}, 500);
});
}).then(function(prevName){
// 여기도 새 Promise를 만든다
return new Promise(function(resolve){
setTimeout(function(){
let name = prevName + ", 누가바";
console.log(name);
resolve(name);
}, 500);
});
});
Promise (2)
Promise (1)의 반복부분 코드를 함수화 한 코드이다.
// let addIce = (name) => {} // 화살표 함수
let addIce = function (name) {
return function(prevName){
// 여기도 새 Promise를 만든다
return new Promise(function(resolve){
setTimeout(function(){
// 백틱 ``
// if (newName) {
// }else {
// }
let newName = prevName ? `${prevName}, ${name}` : name;
console.log(newName);
resolve(newName);
}, 500);
});
};
};
// then (그러면 ~~)
addIce("죠스바")()
.then(addIce("메로나"))
.then(addIce("바밤바"))
.then(addIce("누가바"));
Generator
Generator(함수)를 실행하면 iterator(반복가능) 객체를 반환한다.
순서가 필요한 로직에서 순서보장을 받기위해 비동기적인 요소를 동기적으로 바꾸려고 한다.
// (1) 제너레이터 함수 안에서 쓸 addIce 함수 선언
let addIce = function (prevName, name) {
setTimeout(function () {
iceMaker.next(prevName ? prevName + ', ' + name : name);
}, 500);
};
// *이 붙은 함수가 제너레이터 함수
// 실행하면 -> Iterator 객체가 반환된다.
// (2) 제너레이터 함수 선언
// yield 키워드로 순서 제어
let iceGenerator = function* () {
var ice1 = yield addIce('', '죠스바');
console.log(ice1);
var ice2 = yield addIce(ice1, '메로나');
console.log(ice2);
var ice3 = yield addIce(ice2, '바밤바');
console.log(ice3);
var latte = yield addIce(ice3, '누가바');
console.log(latte);
};
let iceMaker = iceGenerator();
iceMaker.next();
Promise + [async (비동기), await(기다리다)]
Promise ~ then 과 동일한 효과이다.
async 함수 내부에서 실질적인 비동기 작업이 필요한 위치마자 await를 넣어주면 된다.
// iceMaker 함수에서 호출할 함수, 'addICe'를 선언
// Promise를 반환
var addICe = function (name) {
return new Promise(function (resolve) {
setTimeout(function(){
resolve(name);
}, 500);
});
};
// var iceMaker = async () => {} ㅡ> 화살표 함수
var iceMaker = async function () {
var iceMaker = '';
var _addICe = async function (name) {
iceMaker += (iceMaker ? ', ' : '') + await addICe(name);
};
// Promise를 반환하는 함수인 경우, awit를 만나면 무조건 끝날 때 까지 기다린다.
// _addICe('죠스바') 이 로직이 실행되는데 N초가 걸렷다.
await _addICe('죠스바');
// console.log는 N초 뒤 실행
console.log(iceMaker);
await _addICe('메로나');
console.log(iceMaker);
await _addICe('바밤바');
console.log(iceMaker);
await _addICe('누가바');
console.log(iceMaker);
};
iceMaker();