[JAVA SCRIPT] 콜백지옥과 비동기 제어

차슈·2024년 5월 2일
0

JAVA SCRIPT

목록 보기
23/24
post-thumbnail

1. 콜백지옥

콜백함수를 익명함수로 전달하는 과정에서 코드가 들여쓰기 수준 (헬)인 경우

  • 가독성이 지옥
  • 수정도 어려움

2. 동기 vs 비동기

2-1. 동기 Synchronous = sync

실행중인 코드가 끝나야 다음 코드를 실행하는 방식
코드가 동시에 실행됨

  • CPU의 계산에 의해 즉시 처리가 가능한 코드 = 동기적 코드

사진의 위에 부분에 해당

2-2. 비동기 a + synchronous = async

실행중인 코드의 완료 여부와 관계없이 바로 다음 코드로 넘어감
코드가 동시에 실행되지 않음

  • setTimeout, addEventLister
  • 요청, 실행대기, 보류와 관련된 코드

사진의 아래부분에 해당

3. 콜백지옥의 예시와 해결방안

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

비동기처리를 할때, 처리가 끝나면 알려달라는 약속

  • 비 동기를 더 효율적으로 처리하기 위해 사용
  • new 연삱자로 호출한 promise의 인자로 넘어가는 콜백은 바로 실행
  • 내부의 resolve 함수를 호출하는 구문이 있으면 실행되기 전까지 then, catch로 넘어가지 않음
  • 비동기작업이 완료될때 resolve, reject 호출

예시1

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

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

function fetchData() {
  return new Promise((resolve, reject) => {
    // 비동기 작업 수행
    // 성공 시 resolve 호출, 실패 시 reject 호출
  });
}

fetchData()
  .then(data => console.log(data))
  .catch(error => console.log(error));

비동기 작업의 동기적 표현 : Generator

*가 붙은 함수
실행하면 보통 iterator 객체 (반환 next())를 가짐

  • next 메서드 호출 시, Generator 함수 내부에서 가장 먼저 등장하는 yield에서 stop 이후 다시 next 메서드를 호출하면 멈췄던 부분 -> 그 다음의 yield까지 실행 후 stop

  • 비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면 위 -> 아래 순차적으로 돌아감

예시

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();

비동기 작업의 동기적 표현 : Promise + Async/await

비동기를 하려는 함수 앞에 async 함수 내부에 비동기가 필요한 부분마다 await 붙임

= promise ~ then 과 같은 효과

예시1

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();

예시2

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function fetchUser() {
  await delay(2000); // 2초 대기
  return 'Alice';
}

async function greetUser() {
  const user = await fetchUser();
  console.log(`안녕하세요, ${user}님!`);
}

greetUser();

0개의 댓글