[Javascript] Javascript에서의 generate

기본기

Posted by Sub DevLog on February 21, 2021

이전 포스팅에서 봤던 이너레이터가 값을 읽어오기 위한 인터페이스라면 제너레이터는 값을 쓰기 위한 인터페이스이다. 제너레이터는 또한 interator protocol 규약을 준수해서 반복문인 for..of문과 Spread문법에서 사용할 수 있다.

제너레이터 함수

제너레이터는 일반적인 함수와는 다르게 * 함수뒤나 함수명 앞에 * 를 붙입니다.

1
2
3
4
5
function* test() {
  return 1;
}
const gen = test();
console.log(gen);//test {<suspended>}

test 함수를 호출했는데 왜 값이 3 반환이 안될까??

제너레이터 함수는 일반적인 함수와 다르게 함수를 호출하게 되면 함수가 실행이 되지않고 실행을 처리하는 제너레이터 객체가 반환이 됩니다.

generate

이 안에 객체를 사용하기 위해선 next란 메서드와 yield란 객체가 필요합니다.

yield,next

yieldnext는 제너레이터에서의 중요한 메서드입니다. next메서드는 generate함수를 실행시키는 메서드라 생각하시면 됩니다. next메서드를 호출하게 되면 yield<value>문을 만날때까지 실행이 계속됩니다. yield<value>문을 만나게 되면 함수의 실행이 멈추고 value 값이 반환이 됩니다. yield는 제너레이터 함수가 실행될때 debuging모드에서의 break point처럼 중간에 함수를 정지를 시키며 yield 뒤에 오는 표현식이 반환됩니다.

1
2
3
4
5
6
7
8
9
10
11
function* test() {
    yield 1;
    yield 2;

  return 3;
}
const gen = test();
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next());//{value: 2, done: false}
console.log(gen.next());//{value: 3, done: true}
console.log(gen.next());//{value: undefined, done: true}

위 예제에서도 gen함수의 next메서드를 호출하였을 때 순차적으로 yield에 멈추게 되고 yield의 value값이 반환이 됩니다. 마지막으로는 return문에 다다르고 제너레이터가 종료가 됩니다. 제너레이터가 종료된 후 next메서드를 호출하여도 value 값은 undefinded입니다.

제너레이터의 이터러블 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
function* test() {
    yield 1;
    yield 2;

  return 3;
}

let gen = test();

for(let value of gen) {
  console.log(value); // 1, 2가 출력됨
}

제너레이터도 이터러블처럼 fot..of 반복문을 사용해서 값을 얻을 수 있습니다. 하지만 위예제문에서의 return 값은 반환이 안되죠 그 이유는 이터러블의 {value,done}프러퍼티에서 done의 값이 true일 경우 value값은 무시가 되기 때문입니다. 그러므로 반복문에서 제너레이터를 사용할 경우 모든 값은 yield로 반환해주어야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const test = (function* () {
  let cur = 0;

  while (true) {
    cur+=1
    yield cur;
  }
}());

// infiniteFibonacci는 무한 이터러블이다.
for (const num of test) {
  if (num > 10) break;
  console.log(num);
}

또한 Spread문법을 이용하여 작성할수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
const test = function* (max) {
  let cur = 0;

  while (true) {
    cur+=1
    if (cur >= max) return; // 제너레이터 함수 종료
    yield cur;
  }
};

console.log([...test(10)]);

예외처리

제너레이터의 인자값은 yield의 value에 값이 지정됩니다. 하지만 next이외에도 generate에 throw란 메서드를 이용해서 값을 던질 수 있습니다. throw의 경우에는 next메서드와 달리 yield의 값에 지정이 되지 않고 generate의 오류 메세지에 바인딩이 됩니다.

1
2
3
4
5
6
7
8
9
10
function* test() {
  let result = yield 1; // Error in this line
}

let gen = test();

let value = gen.next();

  gen.throw(new Error("에러 발생!!"));

비동기처리

또한 제너레이터는 Promise처럼 비동기 처리를 동기 방식으로 구현할 수 있다. generate함수안에 yield값에 반환되는 비동기 함수를 넣어주면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getUser(genObj, username) {
  fetch(`https://api.github.com/users/${username}`)
    .then(res => res.json())
    .then(user => genObj.next(user.name));
}

const gen = (function* () {
  let user;
  user = yield getUser(g, 'kakao');
  console.log(user); // kakao

  user = yield getUser(g, 'naver');
  console.log(user); // Naver
}());

gen.next();

참고

https://poiemaweb.com/es6-generator https://ko.javascript.info/generators#ref-1932