제너레이터(generator)는 함수의 실행을 중간에 멈추고 재개할 수 있는 기능이다. 실행을 멈출 때 값을 전달할 수 있기 때문에 반복문에서 제너레이터가 전달하는 값을 하나씩 꺼내서 사용할 수 있고 이는 배열이 반복문에서 사용되는 방식과 같다. 값을 미리 만들어 놓지 않고 필요한 순간에 값을 계산해서 전달할 수 있기 때문에 메모리 측면에서 효율적이다.
제너레이터는 별표와 함께 정의된 함수와 그 함수가 반환하는 제너레이터 객체로 구성되고 next, return, throw 메서드를 가지고 있다.
function* f1() { // 1 yield 10; // 2 yield 20; return 'finisihed' } const gen = f1(); // 3 console.log(gen.next()); // 4 // { value: 10, done: false } console.log(gen.next()); // { value: 20, done: false } console.log(gen.next()); // 5 // { value: 'finisihed', done: true } console.log(gen.next()); // 6 // { value: undefined, done: true }
function* f1() { try { // 1 yield 10; yield 20; } cathc (e) { console.log('catch', e); } } const get = f1(); console.log(gen.next()); // { value: 10, done: false } console.log(get.throw('some error')); // 2 // catch some error // { value: undefined, done: true } }
제너레이터 객체는 반복 가능하면서 반복자이다.
- next 메서드를 갖고 있다.
- next 메서드는 value와 done 속성값을 가진 객체를 반환한다.
- done 속성값은 작업이 끝났을 때 참이 된다.
위 조건을 만족하는 객체는 반복자이다.
- Symbol.iterator 속성값으로 함수를 가지고 있다.
- 해당 함수를 호출하면 반복자를 반환한다.
위 조건을 만족하는 객체는 반복 가능(iterable)한 객체이다.
const arr = [10, 20, 30]; const iter = arr[Symbol.iterator](); console.log(iter.next()); // { value: 10, done: false }
Symbol.iterator 속성값을 함수로 갖고 있고 함수가 반환한 iter 변수도 반복자이기 때문에 배열은 반복 가능한 객체다.
function* f1() { yield 10; yield 20; yield 30; } const gen = f1(); console.log(Symbol.iterator]() === gen); // true for (const v of f1()) { // 1 console.log(v); } // 10 // 20 // 30 const arr = [...f1()]; // 2 console.log(arr); // [ 10, 20, 30 ];
Symbol.iterator 속성값을 호출한 결과가 자기 자신(반복자)이다. 따라서 제너레이터 객체는 반복 가능한 객체이고 반복 가능한 객체는 for of 문, 전개 연산자에서 유용하게 쓰인다.
function* g1() { yield 2; yield 3; } function* g2() { yield 1; yield* g1(); yield 4; } console.log(...g2()); // 1 2 3 4
제너레이터 함수에서 다른 제너레이터 함수를 호출할 때는 yield* 키워드를 이용한다.
function* f1() { const data1 = yield; 3️⃣ console.log(data1); // 10 const data2 = yield; 3️⃣ console.log(data2); // 20 } const gen = f1(); gen.next(); 1️⃣ gen.next(10); 2️⃣ gen.next(20); 2️⃣