5. 콜백 지옥과 비동기 제어
1) 콜백 지옥: 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 너무 깊어지는 현상
-비동기적 작업에서 콜백 지옥이 자주 등장
2) 비동기: 동기의 반대말로, 현재 코드 실행 완료 여부와 무관하게 다음 코드 실행
-동기적인 코드는 현재 실행 중인 코드가 완료된 후 다음 코드 실행
-CPU 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적인 코드
-setTimeout, addEventListener, XMLHttpRequest 등 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 비동기적 코드
-웹의 복잡도에 따라 비동기적 코드의 비중이 높아졌고 콜백 지옥에 빠지기 쉬워짐
3) 콜백 지옥 예시
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, '에스프레소');
// 결과
// 에스프레소
// 에스프레소, 아메리카노
// 에스프레소, 아메리카노, 카페모카
// 에스프레소, 아메리카노, 카페모카, 카페라떼
-0.5초마다 커피 이름을 하나씩 추가하여 출력
-들여쓰기가 깊고, 값이 전달되는 순서가 아래에서 위로 어색하게 향함
4) 해결방법
(1) 기명함수로 변환
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, '에스프레소');
-가독성이 높아지고, 위에서 아래로 읽어내려가는대로 출력되어 어색함도 사라짐
-일회성 함수를 매번 변수에 할당하는 것이 복잡해 보일 수 있음
(2) promise
-방법 1
new Promise(function (resolve) {
setTimeout(function () {
var name = '에스프레소';
console.log(name);
resolve(name); // promise가 fufilled(성공)일 때, name 반환
}, 500);
}).then(function (prevName) { // promise.then에서 위의 resolve 결과를 받아 인자로 사용
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 아메리카노'; // 인자로 받은 초기 name에 '아메리카노' 추가
console.log(name);
resolve(name); // 아메리카노가 추가된 name을 다시 반환
}, 500);
});
}).then(function (prevName) { // 위에서 resolve로 넘겨준 아메리카노가 추가된 name을 인자로 사용
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);
});
});
-resolve 또는 reject가 있으면 둘 중 하나가 실행되기 전까지 다음 then 또는 catch로 넘어가지 않으므로 비동기 작업의 동기적 표현이 가능
-방법 2
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('카페라떼'));
-방법 1에서 반복적이던 내용을 함수화해서 짧게 표현
(3) Generator
var addCoffee = function(prevName, name) {
setTimeout(function () {
coffeeMaker.next(prevName ? prevName + ', ' +name : name);
}, 500);
};
var coffeeGenerator = function* () { // Generator 함수
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();
-Generator 함수를 실행하면 Iterator가 반환되며
-Iterator는 next라는 메서드를 가짐
-next를 실행하면 Generator 함수 내 가장 첫번째 yield에서 함수의 실행을 멈춤
-이후 다시 next 메서드 호출하면 멈췄던 부분부터 시작해서 다음 yield에서 함수의 실행을 멈춤
-비동기 작업이 완료되는 시점(console에 name 출력)마다 next를 호출하면 Generator 함수가 위에서 아래로 순차적으로 진행됨
(4) promise + async/await
var addCoffee = function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(name);
}, 500);
});
};
var coffeeMaker = async function () {
var coffeeList = '';
var _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 표기
-await를 쓰려면 함수 앞에 반드시 async가 표기되어 있어야 함
'Front-end > Javascript' 카테고리의 다른 글
[코어 자바스크립트] 05. 클로저(2) (0) | 2022.12.21 |
---|---|
[코어 자바스크립트] 05. 클로저(1) (0) | 2022.12.20 |
[코어 자바스크립트] 04. 콜백 함수(1) (0) | 2022.12.12 |
[코어 자바스크립트] 03. this(2) (0) | 2022.12.08 |
[코어 자바스크립트] 03. this(1) (0) | 2022.12.05 |