자바스크립트 입문_콜백지옥(callback hell), 비동기(async) 제어
자바스크립트 2021. 5. 8. 17:25(본 포스팅은 위키북스의 '코어자바스크립트' 책을 공부하면서 작성되었습니다_내돈내산)
- 콜백지옥(callback hell): 콜백함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상, 주로 비동기 처리를 위한 작업에서 많이 발생하며 가독성과 유지보수 측면에서 좋지못함.
- 비동기(asyncronous): 현재 실행중인 코드의 완료 여부와 무관하게 즉시 다음코드로 넘어감. 사용자의 요청에 의해 특정 시간이 경과되기 전까지 어떤 함수의 실행을 보류한다거나(setTimeout), 사용자의 직접적인 개입이 있을 때 어떤함수를 실행하도록 대기한다거나(addEventListener), 웹브라우저 자체가 아닌 별도의 대상에 무언가를 요청하고 그에대한 응답이 왔을 때 비로소 어떤 함수를 실행하도록 대기하는 등(XMLHttpRequest), 별도의 요청, 실행대기, 보류 등과 관련된 코드
- 콜백지옥 예시 -
setTimeout(function (name) {
let 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, '에스프레소');
- 들여쓰기가 더해지고, 코드를 읽는방향(값이 전달되는 순서)가 '위에서 아래'가 아닌 '아래에서 위'로 되어있어서 가독성이 매우 떨어지고 어색하게 보여짐.
- 콜백지옥 해결: 기명함수로 전환 -
let coffeeList = '';
const addEspresso = function (name) {
coffeeList = name;
console.log(coffeeList); // "에스프레소"
setTimeout(addAmericano, 500, '아메리카노');
};
const addAmericano = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList); // "에스프레소, 아메리카노"
setTimeout(addMocha, 500, '카페모카');
};
const addMocha = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList); // "에스프레소, 아메리카노, 카페모카"
setTimeout(addLatte, 500, '카페라떼');
};
const addLatte = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList); // "에스프레소, 아메리카노, 카페모카, 카페라떼"
};
setTimeout(addEspresso, 500, '에스프레소');
- 가독성이 올라갔음. 위에서 아래로 순서대로 읽어내려갈 수 있도록 구조가 바뀜.
- promise, Generator, async/await 등을 이용해 개선이 가능함.
- Promise를 이용한 비동기 작업의 동기적 표현-
new Promise(function (resolve) {
setTimeout(function () {
const name = '에스프레소';
console.log(name);
resolve(name);
}, 500);
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
const name = prevName + ', 아메리카노';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
const name = prevName + ', 카페모카';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
const name = prevName + ', 카페라떼';
console.log(name);
resolve(name);
}, 500);
});
})
- new 연산자와 함꼐 호출한 Promise의 인자로 넘겨주는 콜백함수는 호출시 바로 실행되지만 resolve 또는 reject함수를 호출하지 않으면 then을 통해 다음으로 넘어가지 않음.(resolve또는 reject가 가지고 있는 인수는 다음 프로미스 구문으로 전달됨)
- 비동기 작업이 완료될때 resolve또는 reject가 호출되는 방법으로 비동기 작업을 동기식으로 표현하는 방법임.
- Generator를 이용한 비동기 작업의 동기적 표현 -
const addCoffee = function (prevName, name) {
setTimeout(function () {
coffeeMaker.next(prevName ? prevName + ', ' + name : name); // 삼항연산자
}, 500);
};
const coffeeGenerator = function* () { // Generator 함수
const espresso = yield addCoffee('', '에스프레소');
console.log(espresso);
const americano = yield addCoffee(espresso, '아메리카노');
console.log(americano);
const mocha = yield addCoffee(americano, '카페모카');
console.log(mocha);
const latte = yield addCoffee(mocha, '카페라떼');
console.log(latte);
};
const coffeeMaker = coffeeGenerator();
coffeeMaker.next(); // 처음 coffeeMaker함수를 실행시키기 위한 next 메소드
- 앞에 * 이 붙은 Generator 함수를 실행하면 Iterator 가 반환되고 Iterator는 next 메서드를 가지고 있음.
- next 메서드가 호출되면 Generator 함수의 제일 첫번째 yield에서 함수의 실행을 멈춤. 다음 next메서드를 한번 더 호출하면 그 다음 yield 에서 멈추고 이를 반복한다. 비동기 작업이 완료되는 시점마다 next 메서드를 호출하면 Generator가 위에서 아래로 순차적으로 진행됨.
- Promise + Async/await 를 이용한 비동기 작업의 동기적 표현 -
const addCoffee = function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(name);
}, 500);
});
};
const coffeeMaker = async function () {
let coffeeList = '';
const _addCoffee = async function (name) {
coffeeList += (coffeeList ? ',' : '') + await addCoffee(name);
};
await _addCoffee('에스프레소');
console.log(coffeeList);
await _addCoffee('아메리카노');
console.log(coffeeList);
await _addCoffee('카페모카');
console.log(coffeeList);
await _addCoffee('카페라떼');
console.log(coffeeList);
};
coffeeMaker();
- 비동기 작업을 수행하고자 하는 함수 앞에 async를 표기하고, 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 표기하는것으로 뒤의 내용을 Promise로 자동전환하고, 해당내용이 resolve된 이후에 다음으로 진행하게 됨.
'자바스크립트' 카테고리의 다른 글
자바스크립트 입문_클로저와 메모리 관리 (0) | 2021.05.09 |
---|---|
자바스크립트 입문_클로저의 의미 및 원리 (0) | 2021.05.08 |
자바스크립트 입문_콜백함수(callback function) 내부의 this에 다른 값 바인딩 하기 (0) | 2021.05.08 |
자바스크립트 입문_콜백함수(callback function)의 제어권 (0) | 2021.05.08 |
자바스크립트 입문_콜백함수(callback function) 개념 (0) | 2021.05.05 |