본문 바로가기
카테고리 없음

콜백함수와 동기/비동기

by riversun1 2024. 4. 26.

 

  콜백 함수와 동기/비동기 처리  

 

🚩 [학습 목표]

  1. 자바스크립트의 콜백함수의 개념과 예시에 대해 학습해요.
  2. 어떤 항목에서 콜백함수를 전달받은 함수에게 제어권이 이양되는지를 이해하고 그 예시를 테스트해볼 수 있습니다.
  3. 비동기적인 코드 작성에서 발생할 수 있는 콜백지옥을 예방하고 비동기제어를 할 수 있는 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) 콜백함수란 

  1. 다른 코드의 인자로 넘겨주는 함수에요! 인자로 넘겨준다는 얘기는 콜백함수를 넘겨받는 코드가 있다는 얘기겠죠. 젖우리가 써본 것 처럼 forEach, setTimeout 등이 되겠네요.
  2. 콜백 함수를 넘겨받은 위와 같은 코드 forEach, setTimeout 등은 이 콜백 함수를 필요에 따라 적절한 시점에 실행하게 돼요. (제어권이 그들에게 있는거죠)
  3. (코어자바스크립트의 예시) 다음 날 친구와 8시에 만나기로 한 스펀지밥 😂
    1. (현명하지 않은) 알람을 안 맞춘 스펀지밥..
      1. 불안한 마음으로 계-속 깨요
      2. 밤새 몇시인지 확인하느라 뒤척여 컨디션이 좋지 않아요
      3. 수시로 시간을 구하는 함수를 직접 호출했어요 (제어권 : 스펀지밥)
      4. 시간을 구하는 함수 : 눈 뜨고 → 일어나고 → 시계 보고 → 아직 안됐네? → 다시 눕고 → 눈 감고 → 잠 들고
    2. (현명한) 알람을 맞춘 스펀지밥
      1. 알람시계를 세팅하고 꿀잠을 자요!
      2. 6시에 알람소리를 듣고 상쾌하게 일어나 컨디션이 너무 좋아요
      3. 시계의 알람을 설정하는 함수(알람시계)를 호출했고, 그 함수(알람시계)가 ‘알아서’ 스펀지밥이 정해놓은 시간이 됐을 때 ‘알람이 울리는 결과’를 반환했어요 (제어권 : 함수 즉, 알람시계) → action에 대한 제어권은 함수에게 있었어요 😎
  4. callback = call(부르다) + back(되돌아오다) = 되돌아와서 호출해줘!
  5. 💡 다시 말하면, 제어권을 넘겨줄테니 너가 알고 있는 그 로직으로 처리해줘!
  6. 즉, 콜백 함수는 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수. 콜백 함수를 위임받은 코드는 자체적으로 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행 ← 이 적절한 시점 역시 제어권이 있는 위임받은 코드가 알아서 하겠네요 👍

 

 (2) 제어권 

다양한 예를 통해서 제어권에 대해 자세히 살펴보도록 합시다. 어떠한 제어권을 콜백함수를 넘겨받은 코드는 갖게될까요?

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

 

위 설명처럼, 아래 예시에서는 콜백 함수의 제어권을 넘겨받은 코드(=setInterval)가 언제 콜백함수를 호출할지에 대한 제어권을 가지게 돼요! 0.3초라는 적절한 시점을 본인의 함수에 적어놓은대로 실행하는 거죠.

 

 setInterval() : 반복해서 매개변수로 받은 콜백함수의 로직을 수행

var count = 0;

// timer : 콜백 내부에서 사용할 수 있는 '어떤 게 돌고있는지'
// 알려주는 id값
var timer = setInterval(function() {
	console.log(count);
	if(++count > 4) clearInterval(timer); // timer 값을 없애줘 = timer을 멈춰줘
}, 300); // 0.3초 간격으로 찍힌다.


// 0
// 1
// 2
// 3
// 4

 

var count = 0;
var cbFunc = function () {
	console.log(count);
	if (++count > 4) clearInterval(timer);
};
var timer = setInterval(cbFunc, 300); // 콜백함수를 가져간 setInterval에 제어권이 있다.

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

 

그 모습을 아래 표 처럼 정리해봤습니다

→ 원래 cbFunc()를 수행한다면 그 호출주체 제어권은 모두 사용자가 되죠.

→ setInterval로 넘겨주게 되면 그 호출주체제어권은 모두 setInterval이 돼요.

code 호출 주체 제어권
cbFunc(); 사용자 사용자
setInterval(cbFunc, 300); setInterval setInterval

 

 

 

  1. 인자
  2. map 함수는 각 배열 요소를 변환하여 새로운 배열을 반환합니다.
  3. 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에 담고 있네요!
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 함수의 규칙에 맞게 ‘나는’ 호출해야 합니다!

  1. this 💡 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.내부적으로는 어떻게 이걸 가능케 하는 것일까요? 별도의 this를 지정하는 방식을 이해하기 위해서. 그리고, 제어권에 대한 이해를 높이기 위해서 map함수를 직접 구현해 볼까요?
  2. 핵심은 call, apply에 있어요!
  3. 앞전 시간에서 우리는 콜백 함수도 함수이기 때문에 기본적으로는 this가 전역객체를 참조한다 라고 귀가 아프도록 들어왔어요. 하지만, 예외사항 기억 나세요? 🙄
// Array.prototype.map을 직접 구현해봤어요!
// map 함수는 항상 두번째 인자로 this를 받는다 = thisArg
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]);
    mappedArr[i] = mappedValue;
  }
  return mappedArr;
};

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

console.log(a);

 

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

아래 코드는 우리가 3주차에서 보았던 코드에요.

 

💡 (다시 한번) 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 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); // this = obj객체 전체
	}
};

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

//callback => obj를 this로 하는 메서드를 그대로 전달한게 아니에요
//단지, obj.logValues가 가리키는 함수만 전달한거에요(obj 객체와는 연관이 없습니다)
// forEach의 속성 = 1번째 인자에 값, 2번째 인자에 index
[4, 5, 6].forEach(obj.logValues);

 

 

 

 

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

🤔 콜백 함수 내부에서 this가 문맥에 맞는 객체를 바라보게 할 수는 없을까요?

🤔 콜백 함수 내부의 this에 다른 값을 바인딩하는 방법

 

<전통적 방식>

우리가 이전에 강제로 this를 제어하는 방법에서 살짝 다뤘던 방식이에요 👀

var obj1 = {
	name: 'obj1',
	func: function() {
		var self = this; //이 부분!
		return function () { // 함수를 또 리턴?
			console.log(self.name);
		};
	}
};

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

 

 

클로저 : 실제 함수가 끝났음에도 불구하고 영향력을 끼친다.

 

실제로는 this를 사용하는게 아니기도 하고, 번거롭네요.

그렇다면, 콜백 함수 내부에서 아에 this를 사용하지 않는다면 어떨까요?

 

// 결과만을 위한 코딩 = 하드코딩 = bad
var obj1 = {
	name: 'obj1',
	func: function () {
		console.log(obj1.name);
	}
};
setTimeout(obj1.func, 1000);

 

첫 번째 예시보다는 훨씬 간결하긴 해요. 하지만 this를 사용하지 않으면서 결과만을 위한 코딩이 되어버렸네요. this를 이용해서 다양한 것을 할 수 있는 장점을 놓치게 되었어요 ㅠㅠ

오히려, 첫 번째 예시를 재활용하는 방향으로 선회해볼까요?

코드가 헷갈릴 수 있으니, VSCode 상에서 직접 function을 대입해보면서 순서를 따라가시기 바래요!

 

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

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

// obj1의 func를 직접 아래에 대입해보면 조금 더 보기 쉽습니다!
var obj2 = {
	name: 'obj2',
	func: obj1.func
};
var callback2 = obj2.func();
setTimeout(callback2, 1500);

// 역시, obj1의 func를 직접 아래에 대입해보면 조금 더 보기 쉽습니다!
var obj3 = { name: 'obj3' };
var callback3 = obj1.func.call(obj3);
setTimeout(callback3, 2000);

위 방법은 조금 번거롭긴 해도 this를 우회적으로나마 활용하여 원하는 객체를 바라보게 할 수 있었어요!

지금까지 전통적인 방법으로 해결했던 모습을 보여드렸어요. 하지만 이제는 이러한 부분을 아주 쉽게 해결할 수 있는데요. 바로 bind 메서드를 이용하는 방법이에요.

 

 

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

var obj1 = {
	name: 'obj1',
	func: function () {
		console.log(this.name);
	}
};
//함수 자체를 obj1에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj1로 고정해줘!
setTimeout(obj1.func.bind(obj1), 1000);

var obj2 = { name: 'obj2' };
//함수 자체를 obj2에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj2로 고정해줘!
setTimeout(obj1.func.bind(obj2), 1500);

 

 

 

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

콜백지옥이란

  1. 콜백 함수를 익명 함수(아래 이미지로 살짝 보시면…)로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 헬 수준인 경우를 말해요! 😂😂😂
  2. 주로 이벤트 처리서버 통신과 같은 비동기적 작업을 수행할 때 발생하죠.
  3. 뭐가문제일까요? 가독성이 정말 지옥(hell)이구요. 오랜 상태로 이렇게 짜여왔기 때문에, 수정도 어렵습니다.
  4. 보통 서버랑 통신할때 사용

가독성과 유지보수 측면에서 엉망.

 

동기 vs 비동기

  1. 동기와 비동기의 개념

주문한 후에 커피가 나올때까지 기다려주세요! (동기) VS 주문한 후에 진동벨이 울리면 커피를 가지러 오세요! (비동기)

 

비동기 - 먼저 나온거만 빨리빨리 처리

 

 

 동기 : synchronous 

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

 

 비동기 : a + synchronous ⇒ async라고들 흔히 부르죠 

  1. 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
  2. setTimeout, addEventListner 등
  3. 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드
  1. 웹의 복잡도가 올라갈 수록 비동기적 코드의 비중이 늘어납니다.
  2. 콜백지옥의 예시와 해결방안
  3. 우리가 그래도 조금은 익숙한 setTimeout을 통해 콜백 지옥의 간단한 예시를 살펴볼게요!
  4. setTimeout이 대표적인 비동기 코드
// setTimeout 함수의 동작원리
// 정해진 시간만큼 기다리고 내부로직 수행
setTimeout(function(){
	// 기본적으로 1000ms이 지나야 여기 로직이 실행이 된답니다 :)
	console.log('hi');
}, 1000);

 

둘다 주문을 먼저 받고 윗 코드는 1초가 지나고 실행되고 아래 코드는 바로 실행

 

 

아래 예시코드는 다음과 같은 문제가 있는데요, 한번 볼까요?

  • 들여쓰기 수준 📉
  • 값 전달 순서 : 아래 → 위
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, Generator(ES6-2015), async/await(ES7)같은 것들이죠.

 

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

 

 

💡 비동기 작업

- 순서를 보장하지 X

 

만약 네이버에서 날씨 정보를 받아오고 그걸 토대로 다음에서 지도 정보를 업데이트 하고싶을 경우.

무조건 네이버가 첫번째로 수행되어야 하고, 다음이 두번째로 수행되어야 한다.

네이버에서 날씨 정보가 다 안왔는데 다음 지도 정보가 와버리면 문제가 생김.

순서를 보장하지 않지만 순서를 보장하는 것처럼. 해야함

 

일의 순서를 보장하도록 만들다 보니 이런식으로 ">" 코드가 만들어 지는 것임. (콜백지옥)

 

 

 

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

💡 Promise에 대해

사실, Promise를 지금 완벽히 이해하기는 정말 어려운 일이다.

비동기 작업의 동기적 표현이라는 주제를 이해할 수 있을 만큼 열심히 공부해봐요🙂

 

 

Promise는 비동기 처리에 대해, 처리가 끝나면 알려달라는 ‘약속’이에요.

  • new 연산자로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행돼요.
  • 그 내부의 resolve(또는 reject) 함수를 호출하는 구문이 있을 경우 resolve(또는 reject) 둘 중 하나가 실행되기 전까지는 다음(then), 오류(catch)로 넘어가지 않아요.
  • 따라서, 비동기작업이 완료될 때 비로소 resolve, reject 호출해요.

우리는 이 방법으로 비동기 -> 동기적 표현을 구현할 수 있습니다 🙂

 

new Promise(function (resolve) { // function(resolve)가 성공했을 때, 다음인 .then으로 넘어간다.
	setTimeout(function () {
		var name = '에스프레소';
		console.log(name);
		resolve(name);
	}, 500); // 0.5초 후에 안에 내용들을 실행해줘
}).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에서 다루게 될거에요!

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) - Generator> 

💡 이터러블 객체(Iterable)

여기선 제너레이터 문법이 등장합니다.

*가 붙은 함수가 제너레이터 함수입니다.

제너레이터 함수는 실행하면, Iterator 객체가 반환(next()를 가지고 있음)돼요.

 

제너레이터는 반복할 수 있는 Iterator 객체를 생성한다.

Iterator 객체는 Next라는 메서드를 가지고 있습니다.

Iterator 객체는 Next라는 메서드로 자기 자신에 있는 요소들을 계속해서 순환할 수 있는 구조로 되어있습니다.

하나하나 순환하면서 어떤 작업을 수행하기에 용이하다.

 

iterator 은 객체는 next 메서드로 순환 할 수 있는 객체구요.

next 메서드 호출 시, Generator 함수 내부에서 가장 먼저 등장하는 yield에서 stop 이후 다시 next 메서드를 호출하면

멈췄던 부분 -> 그 다음의 yield까지 실행 후 stop

 

* yield : 양보하다, 미루다

순서를 기다리지 않는 비동기적인 것들을 양보하고 미루면서 좀 기다리게하는.

그런 역할을 하게 하려고 제너레이터를 사용할 수 있다.

 

즉, 비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면

Generator 함수 내부소스가 위 -> 아래 순차적으로 진행돼요

 

비동기적인 것을 동기적으로 바꿔야 하는 이유?

: 순서가 보장되지 않을때, 순서 보장이 필요한 로직에서, 순서를 보장받도록 하기 위해

 

실제 예제를 봅시다.

// *가 붙은 함수가 제너레이터 함수입니다.
// 이 함수를 실행하면 -> Iterator 객체가 반환됩니다.

// (1) 제너레이터 함수 안에서 쓸 addCoffee 함수 선언
var addCoffee = function (prevName, name) {
	setTimeout(function () {
		coffeeMaker.next(prevName ? prevName + ', ' + name : name);
	}, 500);
};

// (2) 제너레이터 함수 선언
// yield 키워드로 순서 제어
var coffeeGenerator = function* () {
	var espresso = yield addCoffee('', '에스프레소'); // 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(); // coffeeGenerator를 열고닫으면 Iterator 객체가 반환되고
coffeeMaker.next(); // 실행을 시킴

 

 

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

ES2017에서 새롭게 추가된 async/await 문을 이용했어요. 비동기 작업을 수행코자 하는 함수 앞에 async 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 붙여주면 된답니다.

Promise ~ then과 동일한 효과를 얻을 수 있어요

 

 

// Promise
// 이전에 썼던 방법 : then(그러면~)
// 이번에 쓸 방법 : async(비동기) / await(기다리다)

// coffeeMaker 함수에서 호출할 함수, 'addCoffee'를 선언
// Promise를 반환
var addCoffee = function (name) {
	return new Promise(function (resolve) {
		setTimeout(function(){
			resolve(name);
		}, 500);
	});
};
var coffeeMaker = async function () { // async
	var coffeeList = '';
	var _addCoffee = async function (name) {
		coffeeList += (coffeeList ? ', ' : '') + await addCoffee(name);
	};
    
    // async안에 await를 만난 메서드는 그 메서드가 끝날때까지 기다린다. 
    // 그리고 항상 Promise를 반환해야한다.
    // Promise를 반환하는 함수인 경우, await를 만나면 무조건 끝날때까지 기다린다.
    
    //_addCoffee('에스프레소') 이 로직이 실행되는데 100초가 걸렸다면
	await _addCoffee('에스프레소');
    // console.log는 100초 뒤 실행...
	console.log(coffeeList);
	await _addCoffee('아메리카노');
	console.log(coffeeList);
	await _addCoffee('카페모카');
	console.log(coffeeList);
	await _addCoffee('카페라떼');
	console.log(coffeeList);
};
coffeeMaker();