[JS] Generator

javaScript 기초를 탄탄하게 다져보자

Posted by lim.Chuck on December 23, 2024

[JavaScript]

기초

  1. [JS] Js기초
  2. [JS] Js기초 변수
  3. [JS] Js기초 자료형
  4. [JS] Js기초 형변환
  5. [JS] Js기초 연산자
  6. [JS] Js기초 반복문
  7. [JS] Js기초 switch
  8. [JS] Js기초 function
  9. [JS] Js기초 객체
  10. [JS] Js기초 배열

중급

  1. [JS] Js중급 호이스팅(Hoisting)과 TDZ(Temporal Dead Zone)
  2. [JS] Js중급 생성자함수
  3. [JS] 객체 메소드(Object methods), 계산된 프로퍼티(Computed property)
  4. [JS] 심볼(Symbol)
  5. [JS] WeakMap WeakSet
  6. [JS] 숫자, 수학 method (Number, Math)
  7. [JS] 문자열 메소드(String methods)
  8. [JS] 배열 메소드(Array methods)
  9. [JS] 구조 분해 할당 (Destructuring assignment)
  10. [JS] 매개변수 리스트와 전개 문법(Rest parameters and spread syntax)
  11. [JS] 클로저(Closure)
  12. [JS] setTimeout / setInterval
  13. [JS] call / apply / bind
  14. [JS] 상속, 프로토타입(Prototype)
  15. [JS] 클래스(Class)
  16. [JS] 클로미스(Promise)
  17. [JS] 비동기 처리(Async/Await)
  18. [JS] Generator
  19. [JS] 메모리 릭(Memory Leak)

Generator는 함수의 실행을 중간에 멈췄다가 다시 시작할 수 있는 특별한 함수야! 🎯

1. Generator 기본

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Generator 함수 선언 (function* 사용)
function* numberGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

// Generator 사용
const generator = numberGenerator();

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 }

// for...of로 사용
for (const num of numberGenerator()) {
  console.log(num); // 1, 2, 3
}

2. 값 전달하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function* twoWayGenerator() {
  const a = yield 1; // 첫 번째 next()의 반환값, 두 번째 next()의 인자
  console.log("a:", a);

  const b = yield 2; // 두 번째 next()의 반환값, 세 번째 next()의 인자
  console.log("b:", b);

  return 3; // 마지막 next()의 반환값
}

const gen = twoWayGenerator();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next("hello")); // { value: 2, done: false }, 출력: a: hello
console.log(gen.next("world")); // { value: 3, done: true }, 출력: b: world

3. 비동기 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Promise를 yield하는 Generator
function* fetchUserGenerator() {
  try {
    const user = yield fetch("https://api.example.com/user");
    const profile = yield fetch(`https://api.example.com/profile/${user.id}`);

    return profile;
  } catch (error) {
    console.error("Error:", error);
  }
}

// Generator 실행기
function runGenerator(generator) {
  const gen = generator();

  function handle(result) {
    if (result.done) return Promise.resolve(result.value);

    return Promise.resolve(result.value)
      .then((res) => res.json())
      .then((data) => handle(gen.next(data)))
      .catch((error) => handle(gen.throw(error)));
  }

  return handle(gen.next());
}

// 사용
runGenerator(fetchUserGenerator)
  .then((profile) => console.log(profile))
  .catch((error) => console.error(error));

4. 이터레이터 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Collection {
  constructor() {
    this.items = [];
  }

  add(item) {
    this.items.push(item);
  }

  *[Symbol.iterator]() {
    for (const item of this.items) {
      yield item;
    }
  }
}

const collection = new Collection();
collection.add("A");
collection.add("B");
collection.add("C");

// for...of 사용 가능
for (const item of collection) {
  console.log(item); // "A", "B", "C"
}

// 전개 구문 사용 가능
console.log([...collection]); // ["A", "B", "C"]

5. 무한 시퀀스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 무한 피보나치 수열
function* fibonacci() {
  let prev = 0,
    curr = 1;

  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

// 처음 5개의 피보나치 수
const fib = fibonacci();
for (let i = 0; i < 5; i++) {
  console.log(fib.next().value); // 1, 1, 2, 3, 5
}

// 범위 생성기
function* range(start, end, step = 1) {
  for (let i = start; i <= end; i += step) {
    yield i;
  }
}

// 범위 사용
for (const num of range(0, 10, 2)) {
  console.log(num); // 0, 2, 4, 6, 8, 10
}

6. 실전 예제: 페이지네이션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class DataSource {
  constructor() {
    this.data = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`);
  }

  *paginate(pageSize = 10) {
    for (let i = 0; i < this.data.length; i += pageSize) {
      yield this.data.slice(i, i + pageSize);
    }
  }
}

// 사용 예시
const ds = new DataSource();
const paginator = ds.paginate(10);

// 첫 번째 페이지
console.log(paginator.next().value); // ["Item 1", ..., "Item 10"]

// 두 번째 페이지
console.log(paginator.next().value); // ["Item 11", ..., "Item 20"]

// 모든 페이지 순회
for (const page of ds.paginate(10)) {
  console.log("페이지:", page);
}

7. Generator 조합

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function* generateAlpha() {
  yield "A";
  yield "B";
  yield "C";
}

function* generateNum() {
  yield 1;
  yield 2;
  yield 3;
}

function* combined() {
  yield* generateAlpha(); // 다른 Generator 위임
  yield* generateNum();
}

// 사용
for (const value of combined()) {
  console.log(value); // A, B, C, 1, 2, 3
}

꿀팁! 🍯

  1. Generator와 Promise 조합
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function* asyncGenerator() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();

  for (const item of data) {
    yield item;
  }
}

// 사용
async function processData() {
  for await (const item of asyncGenerator()) {
    console.log(item);
  }
}
  1. 에러 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function* errorGenerator() {
  try {
    yield 1;
    throw new Error("Something went wrong");
    yield 2; // 실행되지 않음
  } catch (error) {
    console.log("Error caught:", error.message);
    yield "Error handled";
  }
}

const gen = errorGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // 출력: Error caught: Something went wrong
// { value: 'Error handled', done: false }

이제 Generator는 마스터! 😎 복잡한 이터레이션과 비동기 처리를 우아하게 다룰 수 있을 거야!