본문 바로가기

이터레이터와 제너레이터

iterable·iterator 프로토콜과 next 반환 계약을 이해하고 generator와 yield로 중단 가능한 순회를 만듭니다.

우리는 6장에서 forEach, map, filter 같은 고차 함수로 데이터를 반복 처리하는 방법을 배웠습니다.

하지만 이런 메서드는 배열 중심이므로, Map, Set, String 같은 다양한 반복 가능(iterable) 구조를 일관되게 순회하려면 별도 개념이 필요합니다.

이 질문에 대한 답이 바로 이터레이터(Iterator)와 제너레이터(Generator)입니다.

이터레이터는 자바스크립트의 모든 반복 가능한 객체를 표준화된 방식으로 순회할 수 있도록 해주는 프로토콜이며, 제너레이터는 이 이터레이터를 쉽게 생성할 수 있도록 돕는 특별한 함수입니다.

제너레이터는 소비자가 요청한 만큼만 값을 만들고 중간 실행 상태를 유지할 수 있습니다. 여기의 일반 function*가 그 자체로 비동기 처리를 하거나 별도 스레드에서 실행되지는 않습니다.

이번 장에서는 이터레이터/제너레이터 개념, 동작 방식, 활용 상황을 정리합니다.


이터레이터 (Iterator) 프로토콜

먼저 “반복을 시작할 객체”, “현재 순회 위치를 가진 객체”, “한 값을 요청하는 호출”을 나누세요. 배열과 for...of를 사용할 수 있으면 아래 세 역할을 추적할 수 있습니다.

iterable·iterator·generator 대화 — 상태 추적

next 요청마다 generator가 재개→yield 중단하며 value/done과 현재 실행 위치가 바뀐다. 정상 종료와 return 조기 종료/finally를 구분한 고정 소비자/생성기 snapshot이 유용하다.

iterable·iterator·generator 대화

SEQUENCE · ITERATION PROTOCOL

iterable·iterator·generator 대화

제너레이터 객체를 순회하는 예제다. next는 본문을 재개하고 yield 결과를 받는다. 정상 완료와 for…of 본문의 조기 종료는 서로 다른 경로다.

next 요청과 값 반환·두 종료 경로소비자가 iterable에서 iterator를 얻어 next를 요청하면 generator 본문이 재개되고 yield 값과 done:false가 돌아온다. 정상 종료는 본문 끝의 최종 값과 done:true이며 별도의 return 호출은 필수가 아니다. 소비자의 조기 종료는 return을 요청해 이미 시작한 generator의 finally를 실행한다. 정상 종료와 조기 종료는 두 대안 영역이다. 모바일은 생성기 객체와 본문을 한 역할로 묶지만 요청과 반환 방향, 두 종료 대안을 유지한다. next 자체의 오류나 모든 일반 iterator의 정리 보장으로 확대하지 않는다. 소비자for…ofIterable반복 시작 객체Generatoriterator 역할본문yield · finallySymbol.iterator()iterator 반환next() 요청본문 재개yield(value)value · done:false정상: next()본문 끝으로 진행최종 return 값value · done:true반복: done이 false인 동안 요청아래 두 종료는 대안대안 2 · 소비자의 조기 종료조기 종료: return()실행된 본문의 finally
next 요청과 값 반환·두 종료 경로소비자가 iterable에서 iterator를 얻어 next를 요청하면 generator 본문이 재개되고 yield 값과 done:false가 돌아온다. 정상 종료는 본문 끝의 최종 값과 done:true이며 별도의 return 호출은 필수가 아니다. 소비자의 조기 종료는 return을 요청해 이미 시작한 generator의 finally를 실행한다. 정상 종료와 조기 종료는 두 대안 영역이다. 모바일은 생성기 객체와 본문을 한 역할로 묶지만 요청과 반환 방향, 두 종료 대안을 유지한다. next 자체의 오류나 모든 일반 iterator의 정리 보장으로 확대하지 않는다.소비자for…of생성기두 역할반복 객체 요청iterator 반환next() 요청본문 재개value · false정상: next()본문 끝최종 value · true대안 2 · 조기 종료조기 종료: return()finally 정리대안 1 · 정상 종료yield: 반복 원소최종 return: 원소 아님

1 · iterator 획득

consumer가 iterable의 [Symbol.iterator]()를 호출해 generator object를 받는다.

2 · 각 반복

next()가 body를 resume하고, yield(value)가 { value, done: false }로 돌아온다.

A · 정상 완료

generator가 return 또는 끝에 도달하면 다음 결과가 done: true다. return() 호출은 필요하지 않다.

B · consumer 조기 종료

loop body의 break / return / throw는 IteratorClose를 거쳐 iterator의 return()을 호출하고 generator에 있는 finally 정리 코드를 실행한다.

yield 값과 최종 return 값은 다르다

done: false의 value는 for…of 반복 원소가 된다. done: true의 최종 value는 반복 원소로 사용하지 않는다. 제너레이터 객체 하나가 iterable과 iterator 역할을 함께 한다.

next()는 반복 때마다 사용하지만 return()은 정상 경로의 필수 단계가 아니다. consumer가 반복을 조기에 끝낼 때 IteratorClose가 요청한다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
  • 조기 return의 finally 정리는 이미 시작했으며 finally 정리 코드를 가진 본문 generator의 소비자 조기 종료에 한정합니다. 시작 전 return/throw나 임의 iterator에 같은 정리를 보장하지 않습니다.
관계도의 보충 설명
원도표의 전체 관계·조건·설명
구분내용
원도표 설명consumer가 iterable의 [Symbol.iterator]()를 호출해 generator object를 받는다.
원도표 설명next()가 body를 resume하고, yield(value)가 { value, done: false }로 돌아온다.
원도표 설명generator가 return 또는 끝에 도달하면 다음 결과가 done: true다. return() 호출은 필요하지 않다.
원도표 설명loop body의 break / return / throw는 IteratorClose를 거쳐 iterator의 return()을 호출하고 generator에 있는 finally 정리 코드를 실행한다.
원도표 설명next()는 반복 때마다 사용하지만 return()은 정상 경로의 필수 단계가 아니다. consumer가 반복을 조기에 끝낼 때 IteratorClose가 요청한다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
iterator 준비iterator: 시작/대기 / done: 아직 결과 없음iterable에서 iterator를 얻고 next를 호출한다iterable과 실행 iterator는 역할이 다르다
yield 재개현재: yield에 멈춤 / done: falsenext가 중단 지점까지 실행해 value와 done:false를 반환한다다음 호출까지 generator 본문은 진행하지 않는다
종료와 finally정상 완료 done: true / 조기 return 정리: 이미 시작했고 finally 정리 코드를 가진 generator에서 finally 실행정상 종료는 done:true를 반환하며, 시작한 generator의 조기 return은 finally를 수행한다종료 방식과 시작 전 return/throw의 범위를 분리한다

iterable·iterator·generator 대화 — 상태 추적

next 요청마다 generator가 재개→yield 중단하며 value/done과 현재 실행 위치가 바뀐다. 정상 종료와 return 조기 종료/finally를 구분한 고정 소비자/생성기 snapshot이 유용하다.

iterable·iterator·generator 대화

SEQUENCE · ITERATION PROTOCOL

iterable·iterator·generator 대화

제너레이터 객체를 순회하는 예제다. next는 본문을 재개하고 yield 결과를 받는다. 정상 완료와 for…of 본문의 조기 종료는 서로 다른 경로다.

next 요청과 값 반환·두 종료 경로소비자가 iterable에서 iterator를 얻어 next를 요청하면 generator 본문이 재개되고 yield 값과 done:false가 돌아온다. 정상 종료는 본문 끝의 최종 값과 done:true이며 별도의 return 호출은 필수가 아니다. 소비자의 조기 종료는 return을 요청해 이미 시작한 generator의 finally를 실행한다. 정상 종료와 조기 종료는 두 대안 영역이다. 모바일은 생성기 객체와 본문을 한 역할로 묶지만 요청과 반환 방향, 두 종료 대안을 유지한다. next 자체의 오류나 모든 일반 iterator의 정리 보장으로 확대하지 않는다. 소비자for…ofIterable반복 시작 객체Generatoriterator 역할본문yield · finallySymbol.iterator()iterator 반환next() 요청본문 재개yield(value)value · done:false정상: next()본문 끝으로 진행최종 return 값value · done:true반복: done이 false인 동안 요청아래 두 종료는 대안대안 2 · 소비자의 조기 종료조기 종료: return()실행된 본문의 finally
next 요청과 값 반환·두 종료 경로소비자가 iterable에서 iterator를 얻어 next를 요청하면 generator 본문이 재개되고 yield 값과 done:false가 돌아온다. 정상 종료는 본문 끝의 최종 값과 done:true이며 별도의 return 호출은 필수가 아니다. 소비자의 조기 종료는 return을 요청해 이미 시작한 generator의 finally를 실행한다. 정상 종료와 조기 종료는 두 대안 영역이다. 모바일은 생성기 객체와 본문을 한 역할로 묶지만 요청과 반환 방향, 두 종료 대안을 유지한다. next 자체의 오류나 모든 일반 iterator의 정리 보장으로 확대하지 않는다.소비자for…of생성기두 역할반복 객체 요청iterator 반환next() 요청본문 재개value · false정상: next()본문 끝최종 value · true대안 2 · 조기 종료조기 종료: return()finally 정리대안 1 · 정상 종료yield: 반복 원소최종 return: 원소 아님

1 · iterator 획득

consumer가 iterable의 [Symbol.iterator]()를 호출해 generator object를 받는다.

2 · 각 반복

next()가 body를 resume하고, yield(value)가 { value, done: false }로 돌아온다.

A · 정상 완료

generator가 return 또는 끝에 도달하면 다음 결과가 done: true다. return() 호출은 필요하지 않다.

B · consumer 조기 종료

loop body의 break / return / throw는 IteratorClose를 거쳐 iterator의 return()을 호출하고 generator에 있는 finally 정리 코드를 실행한다.

yield 값과 최종 return 값은 다르다

done: false의 value는 for…of 반복 원소가 된다. done: true의 최종 value는 반복 원소로 사용하지 않는다. 제너레이터 객체 하나가 iterable과 iterator 역할을 함께 한다.

next()는 반복 때마다 사용하지만 return()은 정상 경로의 필수 단계가 아니다. consumer가 반복을 조기에 끝낼 때 IteratorClose가 요청한다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
  • 조기 return의 finally 정리는 이미 시작했으며 finally 정리 코드를 가진 본문 generator의 소비자 조기 종료에 한정합니다. 시작 전 return/throw나 임의 iterator에 같은 정리를 보장하지 않습니다.

관계도의 보충 설명

원도표의 전체 관계·조건·설명
구분내용
원도표 설명consumer가 iterable의 [Symbol.iterator]()를 호출해 generator object를 받는다.
원도표 설명next()가 body를 resume하고, yield(value)가 { value, done: false }로 돌아온다.
원도표 설명generator가 return 또는 끝에 도달하면 다음 결과가 done: true다. return() 호출은 필요하지 않다.
원도표 설명loop body의 break / return / throw는 IteratorClose를 거쳐 iterator의 return()을 호출하고 generator에 있는 finally 정리 코드를 실행한다.
원도표 설명next()는 반복 때마다 사용하지만 return()은 정상 경로의 필수 단계가 아니다. consumer가 반복을 조기에 끝낼 때 IteratorClose가 요청한다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
iterator 준비iterator: 시작/대기 / done: 아직 결과 없음iterable에서 iterator를 얻고 next를 호출한다iterable과 실행 iterator는 역할이 다르다
yield 재개현재: yield에 멈춤 / done: falsenext가 중단 지점까지 실행해 value와 done:false를 반환한다다음 호출까지 generator 본문은 진행하지 않는다
종료와 finally정상 완료 done: true / 조기 return 정리: 이미 시작했고 finally 정리 코드를 가진 generator에서 finally 실행정상 종료는 done:true를 반환하며, 시작한 generator의 조기 return은 finally를 수행한다종료 방식과 시작 전 return/throw의 범위를 분리한다

이터레이터 프로토콜은 next() 호출로 순회 결과를 받는 규칙입니다. for...of는 먼저 이터러블에서 이터레이터를 얻고 이 규칙에 따라 순회합니다.

이터레이터의 필수 메서드는 next()입니다. Symbol.iterator 메서드를 제공하는 것은 별도의 이터러블 프로토콜입니다.

이터러블 (Iterable) 객체

이터러블(Iterable) 객체는 자신의 속성 또는 프로토타입 체인에서 호출 가능한 Symbol.iterator 메서드를 제공하는 객체입니다.

이 Symbol.iterator 메서드는 호출될 때 이터레이터(Iterator) 객체를 반환합니다.

자바스크립트의 내장 이터러블 객체

  • Array
  • String
  • Map
  • Set
  • NodeList (DOM API)
  • arguments 객체

for...of 루프는 이터러블 객체를 순회할 때 내부적으로 이터레이터 프로토콜을 사용합니다.

const myArray = [1, 2, 3];
const myString = "hello";
const mySet = new Set([1, 2, 3]);

// 이터러블은 for...of로 순회 가능
for (const item of myArray) {
    console.log(item); // 1, 2, 3
}

for (const char of myString) {
    console.log(char); // h, e, l, l, o
}

for (const item of mySet) {
    console.log(item); // 1, 2, 3
}

이터레이터 (Iterator) 객체

이터레이터 객체는 next()라는 메서드를 가지고 있는 객체입니다.

next()는 결과 객체를 반환해야 합니다. 아래 예시에서는 value와 불리언 done을 사용합니다. 프로토콜상 두 속성은 생략 가능하며, 생략한 done은 false로 해석됩니다. 숫자 자체를 반환하는 next()는 이 계약을 만족하지 않습니다.

  • value: 순회 과정에서 현재 반환되는 값.
  • done: 순회 완료 여부. true인 결과에도 제너레이터의 return 값처럼 최종 value가 있을 수 있지만, for...of는 이를 반복 원소로 사용하지 않습니다.
const numbers = [10, 20, 30];

// 배열의 Symbol.iterator 메서드를 호출하여 이터레이터 객체를 얻습니다.
const iterator = numbers[Symbol.iterator]();

console.log(iterator.next()); // 결과: { value: 10, done: false }
console.log(iterator.next()); // 결과: { value: 20, done: false }
console.log(iterator.next()); // 결과: { value: 30, done: false }
console.log(iterator.next()); // 결과: { value: undefined, done: true } (더 이상 요소가 없음)

사용자 정의 이터러블 만들기

우리가 만든 객체도 Symbol.iterator 메서드를 구현하면 for...of 루프를 사용할 수 있는 이터러블로 만들 수 있습니다.

// 숫자 범위를 나타내는 사용자 정의 이터러블 객체
const range = {
    from: 1,
    to: 5,
    [Symbol.iterator]: function() { // Symbol.iterator 메서드를 구현합니다.
        let current = this.from; // 클로저처럼 from과 to를 기억합니다.
        const last = this.to;

        return { // 이터레이터 객체를 반환합니다.
            next() { // 이터레이터는 next() 메서드를 가져야 합니다.
                if (current <= last) {
                    return { done: false, value: current++ };
                } else {
                    return { done: true };
                }
            }
        };
    }
};

for (const num of range) {
    console.log(num); // 1, 2, 3, 4, 5
}

// 직접 이터레이터 사용도 가능
const rangeIterator = range[Symbol.iterator]();
console.log(rangeIterator.next()); // { value: 1, done: false }

위 range는 유한한 정수 from·to를 가정합니다. for...of가 요청한 첫 이터레이터와 뒤에서 직접 얻은 rangeIterator는 별개라, 직접 호출은 다시 1부터 시작합니다. 반환한 이터레이터에는 Symbol.iterator가 없으므로 for...of (const n of rangeIterator)를 바로 쓸 수는 없습니다. 이터러블인 range와 next()만 가진 순회 상태를 구별하세요.


제너레이터 (Generator)

제너레이터(Generator)는 이터레이터 프로토콜을 훨씬 더 쉽게 구현할 수 있도록 도와주는 특별한 종류의 함수입니다.

제너레이터 함수는 실행을 일시 중지하고 재개할 수 있는 능력을 가지고 있으며, yield 키워드를 사용하여 값을 '생성(yield)'해낼 수 있습니다.

제너레이터 함수 선언

제너레이터 함수는 function* 문법으로 선언합니다.

일반 함수와 달리, 제너레이터 함수를 호출하면 즉시 실행되는 것이 아니라 제너레이터 객체(Generator Object)를 반환합니다.

이 제너레이터 객체는 이터레이터이자 이터러블입니다.

function* simpleGenerator() {
    console.log("제너레이터 시작");
    yield 1; // 첫 번째 값을 생성하고 여기서 일시 중지
    console.log("첫 번째 yield 후");
    yield 2; // 두 번째 값을 생성하고 일시 중지
    console.log("두 번째 yield 후");
    return 3; // 마지막 값 (done: true)
    console.log("return 후 (실행 안 됨)"); // return 이후 코드는 실행되지 않음
}

const generator = simpleGenerator(); // 함수 호출 시 제너레이터 객체 반환

console.log("제너레이터 함수 호출됨, 하지만 아직 실행 안 됨");

console.log(generator.next()); // '제너레이터 시작' 출력, { value: 1, done: false } 반환
console.log(generator.next()); // '첫 번째 yield 후' 출력, { value: 2, done: false } 반환
console.log(generator.next()); // '두 번째 yield 후' 출력, { value: 3, done: true } 반환
console.log(generator.next()); // { value: undefined, done: true } 반환

yield 키워드

  • yield 키워드는 제너레이터 함수 내부에서 사용됩니다.
  • yield는 함수의 실행을 일시 중지하고, yield 뒤에 오는 값을 이터레이터의 value로 반환합니다.
  • 다음에 next()가 호출되면, 함수는 yield가 있었던 지점부터 실행을 재개합니다.

for...of 루프와 제너레이터

제너레이터 객체는 이터러블이기 때문에 for...of 루프에서 직접 사용할 수 있습니다.

function* countGenerator() {
    for (let i = 1; i <= 5; i++) {
        yield i;
    }
}

for (const num of countGenerator()) {
    console.log(num); // 1, 2, 3, 4, 5
}

next() 메서드로 값 전달하기

generator.next(value)를 호출할 때 인자를 전달하면, 이 값은 이전 yield 표현식의 결과값으로 전달됩니다.

이를 통해 제너레이터의 내부 동작에 외부에서 값을 주입하여 제어할 수 있습니다.

function* multiplyGenerator() {
    console.log("제너레이터 시작");
    const num1 = yield; // 첫 번째 yield는 값을 받기 위해 대기
    console.log(`첫 번째 받은 값: ${num1}`);
    const num2 = yield; // 두 번째 yield는 값을 받기 위해 대기
    console.log(`두 번째 받은 값: ${num2}`);
    return num1 * num2;
}

const multiplier = multiplyGenerator();

console.log(multiplier.next());    // 결과: 제너레이터 시작, { value: undefined, done: false }
console.log(multiplier.next(5));   // 결과: 첫 번째 받은 값: 5, { value: undefined, done: false }
console.log(multiplier.next(10));  // 결과: 두 번째 받은 값: 10, { value: 50, done: true }
console.log(multiplier.next());    // 결과: { value: undefined, done: true }

첫 multiplier.next(99)처럼 인수를 넣어도 처음에는 값을 받을 이전 yield가 없어 99는 사용되지 않습니다. 두 번째 호출의 5가 num1, 세 번째 호출의 10이 num2로 들어갑니다. return 50은 최종 결과이므로 직접 next()로는 읽지만 for...of의 반복 원소에는 포함되지 않습니다.

next(value)에서 전달한 값은 지금 막 멈춘 yield의 반환값이 아니라, 직전에 멈춰 있던 yield 표현식의 결과로 들어갑니다.

그래서 제너레이터는 값을 밖으로 내보내는 동시에, 다음 next() 호출에서 값을 다시 받을 수 있습니다.

yield* (Yield Delegation)

yield*는 다른 이터러블(또는 제너레이터)에 순회를 위임할 때 사용합니다.

function* generatorA() {
    yield 1;
    yield 2;
}

function* generatorB() {
    yield 3;
    yield* generatorA(); // generatorA의 모든 yield 값을 내보냄
    yield 4;
}

for (const value of generatorB()) {
    console.log(value); // 3, 1, 2, 4
}

제너레이터의 활용 사례

무한 스크롤 / 무한 데이터 스트림: 필요한 만큼만 데이터를 생성하여 메모리 사용을 효율적으로 관리할 수 있습니다.

function* infiniteSequence() {
    let i = 0;
    while (true) { // 무한 루프
        yield i++;
    }
}

const seq = infiniteSequence();
console.log(seq.next().value); // 0
console.log(seq.next().value); // 1
// 필요한 만큼만 값을 가져오므로 무한히 생성 가능

필요한 만큼 next()를 호출하는 예시입니다. 끝이 없는 제너레이터에 [...seq]나 종료 조건 없는 for...of를 적용하면 끝까지 수집하려다 실행을 계속 점유할 수 있습니다. 예제의 숫자를 무한한 정밀 정수로 보장하는 코드도 아닙니다.

비동기 프로그래밍 (과거의 코루틴 구현): async/await가 도입되기 전에는 제너레이터를 사용하여 비동기 코드를 동기적으로 보이는 것처럼 작성하는 패턴(코루틴)이 유행했습니다.

(co 라이브러리 등)

// (개념적인 예시, 실제 async/await와 비교)
function* fetchUserAndPosts() {
    const user = yield fetch('/api/user').then(res => res.json());
    const posts = yield fetch(`/api/posts?userId=${user.id}`).then(res => res.json());
    return { user, posts };
}

// 이런 제너레이터는 특정 실행기(runner)가 next()를 반복적으로 호출하며 Promise를 처리해줘야 함
// 이것이 async/await로 발전함

위 fetchUserAndPosts는 실행기와 실제 /api/user·/api/posts 서버가 필요한 개념 예시입니다. 함수를 호출하기만 해서는 본문이 실행되지 않고, 단순히 next()를 반복한다고 Promise가 완료될 때까지 자동으로 기다리지는 않습니다. 이 절의 독립 실습은 네트워크 없이 아래 코드로 진행합니다.

상태 관리 (State Management): 유한 스테이트 머신(Finite State Machine)과 같이 상태 변화를 제어하는 데 활용될 수 있습니다.

이터러블 객체 생성의 편의성: 사용자 정의 객체를 for...of로 순회할 수 있도록 만들 때, 이터레이터를 직접 구현하는 것보다 제너레이터를 사용하는 것이 훨씬 간단합니다.


일찍 종료한 순회와 잠시 멈춘 순회

첫 yield까지 실행한 뒤 for...of를 break하면 이터레이터의 return이 호출될 수 있습니다. 제너레이터는 그 종료 요청에서 finally를 실행합니다. 반면 직접 next()를 한 번 호출한 것은 순회를 닫았다는 뜻이 아닙니다.

function* stepsForIteration() {
    try {
        yield 1;
        yield 2;
    } finally {
        console.log("정리");
    }
}
const closedForIteration = stepsForIteration();
for (const value of closedForIteration) {
    console.log("값", value);
    break;
}
console.log("닫힌 결과", closedForIteration.next().done);

출력은 값 1, 정리, 닫힌 결과 true입니다. 그림의 종료 화살표는 소비자가 조기 종료할 때의 return() 요청입니다. 정상적으로 done: true를 받은 모든 경우나, next() 자체가 오류를 던진 경우까지 같은 정리 호출이 추가된다고 일반화하지 않습니다.

독립 연습

같은 stepsForIteration을 사용하되 새 제너레이터 const pausedForIteration = stepsForIteration();를 만드세요. for...of 대신 차례로 console.log(pausedForIteration.next().value);, console.log(pausedForIteration.next().value);, console.log(pausedForIteration.next().done);을 호출합니다. 출력 순서와 정리의 위치를 예상하세요.

다음 값을 직접 요청한 결과

1, 2, 정리, true 순서입니다. 첫 두 호출은 각각 yield에서 멈춥니다. 셋째 호출에서 본문 끝으로 진행하면서 finally를 실행하고 종료합니다. next()를 한 번만 호출한 뒤 더 이상 요청하지 않으면 이 코드의 정리는 아직 실행되지 않습니다. 자원을 소유한 순회라면 종료 책임도 설계해야 합니다.

근거: MDN 반복 프로토콜과 조기 종료, Generator.next의 값 전달.

이터레이터와 제너레이터 정리

이번 장에서는 자바스크립트에서 반복 가능한 객체를 다루는 표준인 이터레이터 프로토콜과, 이 이터레이터를 쉽게 생성하고 함수의 실행 흐름을 제어할 수 있는 제너레이터에 대해 학습했습니다.

배열, 문자열, 맵 같은 반복 가능한 객체는 이터러블 프로토콜을 따릅니다.

Symbol.iterator와 next()를 확인하면 for...of가 값을 순회하는 방식을 직접 이해할 수 있습니다.

더 나아가, function* 문법과 yield 키워드를 사용하여 제너레이터 함수를 만들고, next()를 통해 실행을 일시 중지하고 재개하는 방법을 익혔습니다.

또한, yield*를 이용한 위임과 next()로 값을 전달하는 고급 기능, 그리고 무한 스크롤, 비동기 코루틴 등의 활용 사례를 통해 제너레이터의 잠재력을 엿보았습니다.

제너레이터는 async/await의 등장 이후 비동기 처리의 주요 패턴에서는 덜 사용되지만, 데이터 스트림 처리나 실행 흐름을 세밀하게 제어해야 할 때 여전히 검토할 수 있습니다.

이터레이터와 제너레이터를 이해하면 순회 프로토콜과 지연 실행 구조를 더 정확히 해석할 수 있습니다.

앞의 iterable·iterator·generator 대화 다이어그램은 세 역할이 next()로 협력하는 순서를 보여줍니다.

반복 도구를 선택할 때는 데이터가 이미 있는지, 값을 지연 생성해야 하는지, 외부에서 실행 흐름을 제어해야 하는지를 먼저 확인하면 됩니다.

배열 순회, 이터레이터, 제너레이터, yield*는 데이터 보유 여부, 지연 생성, 위임 요구로 구분해 선택합니다.