본문 바로가기
내일배움 강의/완강-JS 문법 종합반

4주차 : 콜백 함수와 동기/비동기 처리

by GREEN나무 2024. 11. 5.
728x90

[학습 목표]

자바스크립트의 콜백함수의 개념과 예시에 대해 학습해요.
어떤 항목에서 콜백함수를 전달받은 함수에게 제어권이 이양되는지를 이해하고 그 예시를 테스트해볼 수 있습니다.
비동기적인 코드 작성에서 발생할 수 있는 콜백지옥을 예방하고 비동기제어를 할 수 있는 Promise, async/await 등의 기술을 이해하고 활용하는 능력을 배양합니다

1. 콜백함수

// 콜백함수 예시 setTimeout, 배열에 대한 forEach 
// setTimeout
setTimeout(function() {
  console.log("Hello, world!");
}, 1000);

// forEach
const numbers = [1, 2, 3, 4, 5];

numbers.forEach(function(number) {
  console.log(number);
});

이처럼 다른 코드의 인자로 넘겨주는 함수인 콜백함수의 여러가지 특징과 바람직하지 않은 상황(콜백 지옥)을 만났을 때 어떻게 대처해야 할지 배워봅시다. 

동기 vs 비동기의 개념도 학습합니다.

비돈기 적인 것을 동기적으로 보이도록 만드는 과정이 필요합니다.

(1) 콜백함수란

callback = call(부르다) + back(되돌아오다) = 되돌아와서 호출

        forEach, setTimeout 등

콜백 함수는 다른 함수에 인자로 넘겨주는 함수로, 넘겨받은 함수는 적절한 시점에 이를 실행합니다. 즉, 제어권★ 넘겨받은 함수는 그에 맞는 로직을 실행하며, 콜백 함수는 제어권을 위임받은 코드가 결정된 시점에 실행됩니다.

제어권은 콜백함수가 제어권을 위임한 함수에게 있습니다.

제어권을 넘겨줄 테니 네가 아는 로직으로 처리해

 

(2) 제어권


◆ 호출 시점 
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가집니다.

var count = 0;

// timer : 콜백 내부에서 사용할 수 있는 '어떤 게 돌고있는지' 알려주는 id값
// setInterval : 반복해서 매개변수로 받은 콜백함수의 로직을 수행
var timer = setInterval(function() {
	console.log(count);
	if(++count > 4) clearInterval(timer); // clearInterval() : ()안의 변수를 없애라 -> 로직을 빠져나옴
}, 300);
/*
0
1
2
3
4 */
var count = 0;
var cbFunc = function () {
	console.log(count);
	if (++count > 4) clearInterval(timer);
};
// setInterval이 콜백 함수의 제어권을 넘겨받은 코드입니다.
var timer = setInterval(cbFunc, 300);

// 실행 결과
// 0 (0.3sec)
// 1 (0.6sec)
// 2 (0.9sec)
// 3 (1.2sec)
// 4 (1.5sec)


→ 원래 cbFunc()를 수행한다면 그 호출주체와 제어권은 모두 사용자가 됩니다.
setInterval로 넘겨주게 되면 그 호출주체와 제어권은 모두 setInterval이 됩니다.


  인자
map 함수 : 배열에 대한 매소드. 기존 배열을 변경하지 않고, 새로운 배열을 생성합니다.

// map 함수에 의해 새로운 배열을 생성해서 newArr에 담고 있네요!
var newArr = [10, 20, 30].map(function (currentValue, index) {
	console.log(currentValue, index);
	return currentValue + 5;
});
console.log(newArr);

// -- 실행 결과 --
// 10 0
// 20 1
// 30 2
// [ 15, 25, 35 ]


 콜백함수에서 여러분이 넣은 currentValue, index 이 변수의 순서를 바꾸면 ?

// map 함수에 의해 새로운 배열을 생성해서 newArr에 담습니다.
// index, currentValue는 js의 명령어가 아닙니다. 의도한 결과가 나오지 않았습니다.
// 매게변수 자리 주의하세요
var newArr2 = [10, 20, 30].map(function (index, currentValue) {
	console.log(index, currentValue);
	return currentValue + 5;
});
console.log(newArr2);

// -- 실행 결과 --
// 10 0
// 20 1
// 30 2
// [ 5, 6, 7 ]

의도하지 않은 값이 나옵니다.
컴퓨터는 사람이 아니기 때문에, index - currentValue의 의미를 사람처럼 이해할 수 없습니다. 
이처럼, map 메서드를 호출해서 원하는 배열을 얻고자 한다면 정의된 규칙대로 작성하세요.(콜백 내부의 인자도 포함) 이 모든것은 전적으로 map 메서드. 즉, 콜백 함수를 넘겨받은 코드에게 그 제어권이 있습니다. 인자(의 순서)까지도 제어권이 그에게 있습니다..
제어권이 넘어갈 map 함수의 규칙에 맞게 ‘나는’ 호출해야 합니다!

 

◆ this
앞전 시간에서 우리는 콜백 함수도 함수이기 때문에 기본적으로는 this가 전역객체를 참조한다고 배웠습니다.

예외사항도 있었습니다.
      제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.
내부적으로 별도의 this를 지정하는 방식과 제어권에 대한 이해를 높이기 위해서 map함수를 직접 구현해 봅시다.
핵심은 call, apply

// Array.prototype.map을 직접 구현해봤어요!
Array.prototype.mapaaa = function (callback, thisArg) {
  var mappedArr = [];

  for (var i = 0; i < this.length; i++) {
    // call의 첫 번째 인자는 thisArg가 존재하는 경우는 그 객체, 없으면 전역객체
    // call의 두 번째 인자는 this가 배열일 것(호출의 주체가 배열)이므로,
		// i번째 요소를 넣어서 인자로 전달
    var mappedValue = callback.call(thisArg || global, this[i]); // map함수는 항상 callback을 받습니다.
    mappedArr[i] = mappedValue;
  }
  return mappedArr;
};

const a = [1, 2, 3].mapaaa((item) => {
  return item * 2;
});

console.log(a);

바로 제어권을 넘겨받을 코드에서 call/apply 메서드의 첫 번째 인자에서 콜백 함수 내부에서 사용될 this를 명시적으로 binding 하기 때문에 this에 다른 값이 담길 수 있다.

//이젠 이 코드를 좀 더 잘 이해할 수 있어요!!

// ★setTimeout은 내부에서 콜백 함수를 호출할 때, call 메서드의 첫 번째 인자에
// 전역객체를 넘겨요
// 따라서 콜백 함수 내부에서의 this가 전역객체를 가리켜요
setTimeout(function() { console.log(this); }, 300); // Window { ... }

// ★forEach도 마찬가지로, 콜백 뒷 부분에 this를 명시해주지 않으면 전역객체를 넘겨요!
// 만약 명시한다면 해당 객체를 넘기긴 해요!
[1, 2, 3, 4, 5].forEach(function (x) {
	console.log(this); // Window { ... }
});

//★addEventListener는 내부에서 콜백 함수를 호출할 때, call 메서드의 첫 번째
//인자에 addEventListener메서드의 this를 그대로 넘겨주도록 정의돼 있어요(상속)
document.body.innerHTML += '<button id="a">클릭</button';
document.body.querySelector('#a').addEventListener('click', function(e) {
	console.log(this, e);
});

 

(3) 콜백 함수는 함수다

콜백 함수로 어떤 객체의 메서드를 전달하더라도, 그 메서드는 메서드가 아닌 함수로 호출합니다.

var obj = {
	vals: [1, 2, 3],
	logValues: function(v, i) {
		console.log(this, v, i);
	}
};

//method로써 호출
obj.logValues(1, 2);

//callback => obj를 this로 하는 메서드를 그대로 전달한게 아니에요
//단지, obj.logValues가 가리키는 함수만 전달한거에요(obj 객체와는 연관이 없습니다)

[4, 5, 6].forEach(obj.logValues);

//4, 5, 6].forEach(obj.logValues(1,4));처럼 매게변수까지 넣으면 매게변수로 넣는게 함수가 아니라 함수를 실행한 걸과를 넣게 됩니다.
// 콜백한수를 넣는건 함수 그 자체를 넣어야 합니아.
// 첫번째 인자는 기준이 되는 배열의 n번째 요소가 들어가고 그 요소의 인덱스를 매게변수로 잡습니다.
// forEack, map, filter 모두 동일.

[44, 5, 6].forEach(obj.logValues(1,4));처럼 매게변수까지 넣으면 매게변수로 넣는게 함수가 아니라 함수를 실행한 결과를 얻게 됩니다.
콜백함수에 넣는건 함수 그 자체를 넣어야 합니다.
첫번째 인자는 기준이 되는 배열의 n번째 요소가 들어가고 그 요소의 인덱스를 매개변수로 잡습니다.
forEack, map, filter 모두 동일.

 

(4) 콜백 함수 내부의 this에 다른 값 바인딩하기

콜백 함수 내부의 this에 다른 문맥에 맞는 객체값을 바인딩하는 방법

더보기


<전통적 방식>
우리가 이전에 강제로 this를 제어하는 방법에서 살짝 다뤘던 방식입니다.​

var obj1 = {
	name: 'obj1',
	func: function() {
		var self = this; //이 부분!
		return function () {
			console.log(self.name);
		};
	}
};

// 단순히 함수만 전달한 것이기 때문에, obj1 객체와는 상관이 없어요.
// 메서드가 아닌 함수로서 호출한 것과 동일하죠.
var callback = obj1.func();
setTimeout(callback, 1000);

 

var obj1 = {
  name: "obj1",
  
// closure 방식
//  : 현재의 함수가 끝났음에도 불구하고 영향력을 끼친다.
// ex) return에서 참조하는 self에서 출력되는걸 보면 밖에있는 self가 살아있음을 알 수 있다.

  // func속성은 함수를 값으로 가집니다.
  // 그 함수도 함수를 return하겠금 만들었습니다.
  func: function () {
    var self = this; //this를 self 에 할당해서 출력
    return function () {
      console.log(self.name);
    };
  },
};

// 단순히 함수만 전달한 것이기 때문에, obj1 객체와는 상관이 없어요.
// 메서드가 아닌 함수로서 호출한 것과 동일합니다.
var callback = obj1.func(); // callback 이라는 변수를 선언하고 obj1.func를 실행한 결과를 담았습니다.
/* 
var callback = function() { 
	var self = this; 
	console.log(self.name);
}*/

// 실행결과가 아니라 함수를 담으려면 'var callback = obj1.func'이렇게 적었어야 합니다.
setTimeout(callback, 1000);

/*
setTimeout(function() { 
	var self = this; 
	console.log(self.name);
}, 1000);*/

실제로는 this를 사용하는게 아니기도 하고, 번거롭네요.
그렇다면, 콜백 함수 내부에서 아예 this를 사용하지 않는다면?

var obj1 = {
	name: 'obj1',
	func: function () {
		console.log(obj1.name);
	}
};
setTimeout(obj1.func, 1000);

완전 하드코딩이 됩니다... this의 이점을 사용하지도 못합니다.

 

첫 번째 예시 재활용

 this를 우회적으로나마 활용하여 원하는 객체를 바라보게만들기

var obj1 = {
  name: "obj1",
  func: function () {
    var self = this; //이 부분!
    // this를 self에 할당하고
    return function () {
      console.log(self.name); // 그 self를 return 하는
      // 그 sel에서 name을 찍어서 console.log하는 그함수를 리턴하합니다.
    };
  },
};

// ---------------------------------

// 개발하다가 로직이 이해가 안되면 직접 로직을 대입해보세요.
// obj1의 func를 직접 아래에 대입해보면 조금 더 이해가 쉽습니다!

var obj2 = {
  name: "obj2",
  func: function () {
    var self = this;
    return function () {
      console.log(self.name); // 결과
    };
  },
};

// 역시, obj1의 func를 직접 아래에 대입해보면 조금 더 보기 쉽습니다!
// obj2와 다르게 name만 가지고 있고 
// 즉시 실행되는 callback함수를 씁니다.
var obj3 = { name: 'obj3' };
var callback3 = obj1.func.call(obj3);// obj3를 this바인딩하러 넘기면 
                                     // 윗줄(name : "obj3")에서 바로 바인딩 됩니다.
setTimeout(callback3, 2000);



// //var callback2 = obj2.func(); // obj2.func를 실행한 결과
// var callback2 = function () {console.log("obj2"); // 결과
// };

// setTimeout(callback2, 1500);// callback2를 1.5초 후에 실행합니다.

 

 

<가장 좋은 방법 → bind메서드의 활용>

bind메서드 : this를 바인딩 해서 새로운 함수를 반환합니다.

함수를 만들 때 유용합니다.

var obj1 = {
	name: 'obj1',
	func: function () {
		console.log(this.name);
	}
};

// 방법1
// 첫번째 인수로 함수를 넣주고 두번째 인자로 초를 넣어주세요
// 첫번째 인수 함수에 바인드로 새로운 함수 리턴해줌
// setTimeout(function(){}, 1000);
//함수 자체를 obj1에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj1로 고정됩니다
setTimeout(obj1.func.bind(obj1), 1000);

// 방법2
//함수 자체를 obj2에 바인딩
var obj2 = { name: 'obj2' };

//obj1.func를 실행할 때 무조건 this는 obj2로 고정해줘!
setTimeout(obj1.func.bind(obj2), 1500);


(5) 콜백 지옥과 비동기 제어

◆ 콜백지옥
▷ 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 헬 수준인 경우를 말합니다.

  (콜백 함수는 익명함수 입니다.)
▷ 주로 이벤트 처리 및 서버 통신과 같은 비동기적 작업을 수행할 때 발생합니다.
▷ 가독성과 유지보수측면에서 힘이 듭니다.

(출처 : https://preiner.medium.com/callback지옥에-promise-적용하기-d02272ecbabe)

 

◆ 동기 vs 비동기
동기와 비동기의 개념

      ↓동기적 

    ↑ 비동기적 
출처 : https://smallzoodevs-organization.gitbook.io/copy-of-javascript-study/day-05./1.

 

▷ 동기 : synchronous
  -  현재 실행중인 코드가 끝나야 다음 코드를 실행하는 방식.
  - CPU의 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적 코드입니다.
  - 계산이 복잡해서 CPU가 계산하는 데에 오래 걸리는 코드 역시도 동기적 코드입니다.


▷ 비동기 : a + synchronous ⇒ async라고들 흔히 부르죠
  - 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
  -  setTimeout, addEventListner 등
  - 별도의 요청, 실행 대기, 보류  등과 관련된 코드는 모두 비동기적 코드


(출처 : https://velog.io/@mrbartrns/til-16-asynchronous-of-js)
  웹의 복잡도가 올라갈 수록 비동기적 코드의 비중이 늘어납니다 

- 비동기작업의 특징 : 

    1. 순서를 보장하지 않는다.

          언제 결과가, 제어권이 나에게 돌아올 지 모릅니다.

- 비동기작업을 동기적으로 사용할 수 있는 방법이 필요합니다.

          순서를 보장하지 않지만 순서를 보장하는 것 처럼 보일 필요가 있습니다.


◆  콜백지옥의 예시와 해결방안
 setTimeout  콜백 지옥 예시

    들여쓰기 수준 📉​
값 전달 순서 : 아래 → 위

setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
  function (name) {
    var coffeeList = name;
    console.log(coffeeList);

    setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
      function (name) {
        coffeeList += ", " + name;
        console.log(coffeeList);

        setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
          function (name) {
            coffeeList += ", " + name;
            console.log(coffeeList);

            setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
              function (name) {
                coffeeList += ", " + name;
                console.log(coffeeList);
              },
              500,
              "카페라떼" 
            );
          },
          500,
          "카페모카"
        );
      },
      500,
      "아메리카노"
    );
  },
  500,
  "에스프레소"
);


해결방법 1 [기명함수로 변환]

함수의 이름을 왼쪽에 붙여줍니다. 물고 물리면서 결국 끝까지 수행합니다.

/*해결방법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, '에스프레소');

 

 

 

  비동기 작업의 동기적 표현이 필요합니다.  

<비동기 작업의 동기적 표현(1) - Promise(1)>

   Promise, Generator(ES6), async/await(ES7) 등


◆ Promise : 비동기 작업을 동기적 흐름처럼 표현할 수 있도록 하는 기능입니다.

  Promise는 비동기 작업이 완료되면 알려달라는 '약속'이라고 볼 수 있습니다.

 ★ new연산자로 호출한 Promise의 인자로 넘어가는 콜백은 즉시 실행되며,

 ★ 내부에서 resolve(성공) 또는 reject(실패) 함수를 호출하기 전까지는 다음 작업(예: .then, .catch)으로 넘어가지 않습니다.
 비동기 작업이 완료될 때 resolve(성공) 또는 reject(실패) 함수를 호출합니다.


  이를 통해 비동기 작업이 끝나기를 기다렸다가 특정 코드가 실행되도록 할 수 있어, 비동기 작업을 더 직관적이고 동기적인 표현으로 구현할 수 있게 됩니다.

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) - Promise(2)>

직전 예제의 반복부분을 함수화 한 코드에요. trigger를 걸어주기 위해 클로저 개념이 나왔지만, 여기서는 skip 하고, 다음 chapter에서 다루게 될거에요!

/**
 * refactoring : 비 효율적인 코드를 효율적인 구조로 재구성시키는 작업
 * re 다시
 * factoring 코드를 재구성하거나 구조화하는 작업
 */

// var addCoffee = function (name) {
// 화살표 함수 이용 가능
var addCoffee = (name) => {
  return function (prevName) {
    // %%%% 다음으로 실행되는 코드들%%%%
    // 이 안에서도 새롭게 Promise를 만듭니다.
    return new Promise(function (resolve) {
      // ***처음 실행함수*****
      setTimeout(function () {
        //var newName	= prevName + ', ' + name;
        // 백틱으로 문자열 지정    `내용${변수}내용`
        // var newName = `${prevName}, ${name}`; // 처음 실행 시 prevName 없음.
        var newName = prevName ? prevName + ", " + name : name;
        console.log(newName);
        resolve(newName);
      }, 500); // ****여기까지 처음실행함수**** // %%%% 다음으로 실행되는 코드들%%%%
    });
  };
};

addCoffee("에스프레소")() // return 받으려고 () 붙여줌
  .then(addCoffee("아메리카노"))
  .then(addCoffee("카페모카"))
  .then(addCoffee("카페라떼"));

 


<비동기 작업의 동기적 표현(3) - Generator>

  제너레이터 문법
제너레이터 함수는 *가 붙은 함수로, 완료시 Iterator 객체를 반환합니다.

      제너레이터 함수는 반복할 수 있는 Iterator객체를 생성합니다.
​▷ Iterator 객체는 next() 메서드를 사용해 순환할 수 있습니다.

 

◆ 이터러블 객체(Iterable)
​▷ iterator : 스페인어로 형용사 반복될 수 있는, 반복할 수 있는
​▷ next() 메서드를 호출할 때마다 제너레이터 함수 내의 yield 키워드가 있는 부분까지 실행합니다.
​▷ 이후 next()를 다시 호출하면, 멈췄던 위치에서부터 다음 yield(양보하다, 미루다)까지 실행됩니다.

      순서를 기다리지 않는 비동기적 작업을 좀더 미루고 양보하게 만들기 위해 사용합니다

 

◆ 비동기 작업과 Generator
​▷비동기 작업이 완료될 때마다 next() 메서드를 호출하면, Generator 함수는 코드가 위에서 아래로 순차적으로 진행됩니다.

 

/**
 *★ 비동기적 코드를 동기적으로 보이게 만드는 이유 ★ 
 * 순서를 보장할 수 없으니 순서 보장이 필요한 로직에서 순서를 보장받기 위해서 필요합니다.
 *
 * Generator함수가 완료되면 Iterator 객체를 반환합니다.
 * = coffeeMaker가 Iterator 객체를 가지고 있다는 뜻입니다.
 * 
 *  **/

var addCoffee = function (prevName, name) { // addCoffee안에next가 있어서 
	setTimeout(function () {
		coffeeMaker.next(prevName ? prevName + ', ' + name : name);
	}, 500);
};
var coffeeGenerator = function* () { // *가 있는 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);
};

/*
* Generator함수가 완료되면 Iterator 객체를 반환합니다.
* = coffeeMaker가 Iterator 객체를 가지고 있다는 뜻입니다.
* Iterator 객체는 계속해서 순환할 수 있는 객체 입니다.
    yield키워드를 만나면 멈춥니다. yield가 있는 줄이 끝날 때 까지 기다려 주고 
      next를 호출하면 다시 멈췄던 부분으로 갑니다.
        이유 : addCoffee안에next가 있기 때문입니다.
*/
var coffeeMaker = coffeeGenerator(); // coffeeMaker가 Iterator 객체
coffeeMaker.next(); //addCoffee안에next가 있어서 반복호출 됩니다.


<비동기 작업의 동기적 표현(4) - Promise + Async/await>

비동기 작업을 수행코자 하는 함수 앞에 async 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 붙여주면 됩니다.
Promise ~ then과 동일한 효과를 얻을 수 있습니다.

// 이번에 쓸 방법 : Async(비동기) / await(기다리다)
// Promise ~ then과 동일한 효과를 냅니다.

//coffeeMaker함수에서 호츨할 함수 addCoffee 선언
//  Promise를 반환
var addCoffee = function (name) {
	return new Promise(function (resolve) {
		setTimeout(function(){
			resolve(name);
		}, 500);
	});
};


// var coffeeMaker = async function () {
var coffeeMaker = async () => { // 'async'를 화살표 함수로 쓰는 법
	var coffeeList = '';
	var _addCoffee = async function (name) {
		coffeeList += (coffeeList ? ', ' : '') + await addCoffee(name);
	};

	// Promise를 반환하는 전제 하에 await를 만나면 끝날 때 까지 기다려 줍니다.
	// ' _addCoffee('에스프레소');' 로직이 끝나는데100초가 걸렸다면 
	//      그 뒤 consol.log()는 100초 뒤에 시작됩니다.
	// 그 아래 await를 만나면 기다리고 끝나면 다음 실행하고를 반복
	await _addCoffee('에스프레소');
	console.log(coffeeList);


	await _addCoffee('아메리카노');
	console.log(coffeeList);
	await _addCoffee('카페모카');
	console.log(coffeeList);
	await _addCoffee('카페라떼');
	console.log(coffeeList);
};
coffeeMaker();

 

 


숙제

Q. 주어진 코드를 async/await 로 리팩토링 하기

class HttpError extends Error {
  constructor(response) {
    super(`${response.status} for ${response.url}`);
    this.name = 'HttpError';
    this.response = response;
  }
}


function loadJson(url) {
  return fetch(url)
    .then(response => {
      if (response.status == 200) {
        return response.json();
      } else {
        throw new HttpError(response);
      }
    })
}

function koreanMovie() {
    return loadJson(`https://klassic-quote-api.mooo.com/v1/random-quote`)
    .then(res => {
            alert(`${res.author}: ${res.quote}`);
      return res;
    })
    .catch(err => {
      if (err instanceof HttpError && err.response.status == 404) {
        alert("무언가 에러가 발생했군요!");
        return koreanMovie();
      } else {
        throw err;
      }
    });
}

koreanMovie();

 


 

공부할 때 참고하기 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/rest_parameters

 

나머지 매개변수 - JavaScript | MDN

나머지 매개변수 구문을 사용하면 함수가 정해지지 않은 수의 매개변수를 배열로 받을 수 있습니다. JavaScript에서 가변항 함수를 표현할 때 사용합니다.

developer.mozilla.org