Post

JavaScript 제네레이터 (Generator)

JavaScript에서 제네레이터(Generator)는 실행을 중간에 멈췄다가 필요할 때 다시 시작할 수 있습니다. 제네레이터는 function* 키워드로 정의되며, yield 키워드를 사용하여 함수의 실행을 제어합니다. 이 글에서는 제네레이터의 개념, 사용법에 대해 설명합니다.

1. 제네레이터의 개념

제네레이터 함수는 일반 함수와 달리 실행을 중간에 멈출 수 있고, 나중에 다시 시작할 수 있는 함수입니다. 제네레이터 함수는 호출되면 이터레이터(Iterator)를 반환하며, next() 메서드를 호출하여 함수의 실행을 제어할 수 있습니다.

2. 제네레이터 사용법

2.1. 제네레이터 함수의 정의

제네레이터 함수는 function* 키워드를 사용하여 정의합니다. 함수 내에서 yield 키워드를 사용하여 값을 반환하고, 실행을 멈춥니다.

1
2
3
4
5
function* simpleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

2.2. 제네레이터 객체 생성

제네레이터 함수를 호출하면 제네레이터 객체가 반환됩니다. 이 객체는 next() 메서드를 가지고 있으며, next() 메서드를 호출하면 제네레이터 함수가 실행됩니다.

1
2
3
4
5
6
const generator = simpleGenerator(); // 제네레이터 객체 생성

console.log(generator.next());  // { value: 1, done: false }
console.log(generator.next());  // { value: 2, done: false }
console.log(generator.next());  // { value: 3, done: false }
console.log(generator.next());  // { value: undefined, done: true }

yield 키워드는 제네레이터 함수의 실행을 일시 중지하고, 값을 반환합니다. 다음 next() 호출 시, 이전 yield 이후의 코드부터 실행이 재개됩니다.

2.3. yield를 사용한 값 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function* yieldExample() {
  console.log('Start');
  yield 1;
  console.log('첫 번째 yield 이후 실행 됨');
  yield 2;
  console.log('두 번째 yield 이후 실행 됨');
}

const iterator = yieldExample();
console.log(iterator.next()); 
// Start
// { value: 1, done: false }

console.log(iterator.next());
// 첫 번째 yield 이후 실행 됨 
// { value: 2, done: false }

console.log(iterator.next());
// 두 번째 yield 이후 실행 됨
// { value: undefined, done: true }

2.4. return 문

제네레이터 함수에서 return 문을 사용하여 실행을 종료하고 값을 반환할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
function* returnGenerator() {
  yield 1;
  return 2;
  yield 3;  // 이 코드는 실행되지 않습니다.
}

const iterator = returnGenerator();
console.log(iterator.next());  // { value: 1, done: false }
console.log(iterator.next());  // { value: 2, done: true }
console.log(iterator.next());  // { value: undefined, done: true }

3. 제네레이터와 이터레이터

제네레이터는 이터레이터 인터페이스를 가지고 있어서, 배열과 같이 for...of 루프에서 사용할 수 있습니다.

3.1. for…of 와 제네레이터

1
2
3
4
5
6
7
8
9
function* countGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

for (let value of countGenerator()) {
  console.log(value);  // 출력: 1 2 3
}

4. 제네레이터의 응용

4.1. 무한 이터레이터

제네레이터를 사용하여 무한 이터레이터를 만들 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
function* infiniteGenerator() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

const infinite = infiniteGenerator();
console.log(infinite.next().value);  // 출력: 0
console.log(infinite.next().value);  // 출력: 1
console.log(infinite.next().value);  // 출력: 2

4.2. 제네레이터를 사용한 비동기 작업

제네레이터는 비동기 작업을 동기식으로 작성하는 데 사용할 수 있습니다. 예를 들어, Promise와 함께 사용하여 비동기 작업을 순차적으로 처리할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function callApi1() {
  return new Promise(resolve => setTimeout(() => resolve(1), 1000));
}

function callApi2() {
  return new Promise(resolve => setTimeout(() => resolve(2), 1000));
}

function* asyncGenerator() {
  const result1 = yield callApi1(1000);
  console.log(result1);  // 출력: 1
  const result2 = yield callApi2(1000);
  console.log(result2);  // 출력: 2
}

const iterator = asyncGenerator();

function process(iteration) {
  if (iteration.done) return; // 종료
  iteration.value.then(value => process(iterator.next(value)));
}

process(iterator.next());

5. throw()

제네레이터 객체의 throw() 메서드를 사용하여 제네레이터 함수 내에서 예외(Exception)를 발생시킬 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
function* throwGenerator() {
  try {
    yield 1;
    yield 2;
  } catch (e) {
    console.log('Error caught inside generator:', e);
  }
}

const iterator = throwGenerator();
console.log(iterator.next());  // { value: 1, done: false }
console.log(iterator.throw(new Error('Something went wrong')));  // Error caught inside generator: Error: Something went wrong
console.log(iterator.next());  // { value: undefined, done: true }
This post is licensed under CC BY 4.0 by the author.