// Generator : 함수의 실행을 중간에 멈췄다가 재개할 수 있는 기술이다.
// function 옆에 * 을 붙여줌으로써 사용한다.  yield 에서 함수의 실행을 멈출 수 있다.
// Generator 함수는 Generator 객체를 반환한다. Generator 객체는 next, return, throw 메소드가 있다.

function* fn() {
  try {
    console.log(1);
    yield 1;
    console.log(2);
    yield 2;
    console.log(3);
    console.log(4);
    yield 3;
    return "finish";
  } catch (e) {
    console.log(e);
  }
}

const a = fn();
a.next(); // 가장 가까운 yield 문을 만날때까지 실행되고 객체를 반환한다.  1출력
a.next(); // 2 출력
//a.return(); // 함수를 바로 끝낸다.
//a.next(); // 나오지 않는다. value 는 undefined 되어 있을 것이다.
a.throw(new Error("Error")); //Error: Error
//at main.js:23
// 함수를 끝낸다. value 는 undefined 되어 있을 것이다.
a.next(); // 값을 받을 수 없다.
// iterable : 반복이 가능하다는 의미이다.
// 반복에는 조건이 필요하다. Symbol.iterator 메소드가 있어야 한다.
// Symbol.iterator 는 iterator 를 반환해야 한다.

// iterator 는 value 와 done property를 가진 객체를 반환하는 메소드 next 가 있어야 한다.
//배열을 살펴보자.

const arr = [1, 2, 3, 4, 5];
//console.log(arr); // proto 를 보면  Symbol(Symbol.iterator): ƒ values()
const it = arr[Symbol.iterator]();
console.log(it.next()); // {value: 1, done: false}
console.log(it.next()); // {value: 2, done: false}
console.log(it.next()); // {value: 3, done: false}
console.log(it.next()); // {value: 4, done: false}
console.log(it.next()); // {value: 5, done: false}
console.log(it.next()); // {value: undefined, done: false}
// 배열은 반복 가능한 객체라는 것이 확인된다.
// 문자열도 된다.
function* fn() {
  yield "hoon";
  yield "malza";
  yield "chulsoo";
}

const a = fn();

console.log(a[Symbol.iterator]() === a); // true
// generator 는 iterable 객체다.

for (const num of a) {
  console.log(num); // hoon, malza, chulsoo 잘 나온다.
}
// next 에 인자 넘기기

function* fn() {
  const num1 = yield "첫 번째 숫자를 입력 해 주세요";
  console.log(num1);

  const num2 = yield "두 번째 숫자를 입력 해 주세요";
  console.log(num2);

  return num1 + num2;
}

const a = fn();
console.log(a.next()); // {value: "첫 번째 숫자를 입력 해 주세요", done: false}
console.log(a.next(1)); // {value: "두 번째 숫자를 입력 해 주세요", done: false}
console.log(a.next(3)); // {value: 4, done: true}
// Generator 는 값을 미리 만들어 두지 않는다. 메모리 측면에서 효율적이라고 볼 수 있다.
// 필요한 순간에만 연산해서 값을 넘겨준다.

function* fn() {
  let index = 0;
  while (1) {
    yield index++;
  }
}

const a = fn();
console.log(a.next()); // {value: 0, done: false}
console.log(a.next()); // {value: 1, done: false}
console.log(a.next()); // {value: 2, done: false}
console.log(a.next()); // {value: 3, done: false}
console.log(a.next()); // {value: 4, done: false}

// generator 가 아니었다면 break 없는 while 문은 프로그램을 뻗게 했을 것이다.
// yield* 로 다른 Generator 를 불러보자.
function* fn1() {
  yield "w";
  yield "o";
  yield "r";
  yield "l";
  yield "d";
}

function* fn2() {
  yield "hello! ";
  //yield* fn1(); // 반복 가능한 모든 객체가 올 수 있다.
	yield* [1, 2, 3, 4]; // hello!  1 2 3 4 ! 이렇게도 된다.
  yield "!";
}

const a = fn2();
console.log(...a); // hello!  w o r l d !