function*
function* (생성기 함수 선언)
function* 선언은 주어진 이름에 새로운 생성기 함수(generator function) 바인딩을 만듭니다. 생성기 함수는 함수를 빠져나갔다가 나중에 다시 진입할 수 있으며, 재진입 사이에 문맥(변수 바인딩)이 보존됩니다. 원문은 MDN의 JavaScript 참고 문서입니다.
본문
function* 선언은 주어진 이름에 새로운 생성기 함수 바인딩을 만듭니다. 생성기 함수는 빠져나갔다가 나중에 다시 진입할 수 있습니다. 함수* 표현식(function* expression)으로도 생성기 함수를 정의할 수 있습니다.
다음은 "Try it" 예제입니다.
function* generator(i) {
yield i;
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value); // Expected output: 10
console.log(gen.next().value); // Expected output: 20
문법 (Syntax)
function* name(param0) {
statements
}
function* name(param0, param1) {
statements
}
function* name(param0, param1, /* …, */ paramN) {
statements
}
name: 함수 이름입니다.param(선택): 함수의 형식 매개변수 이름입니다.statements(선택): 함수 본문을 구성하는 문들입니다.
참고: 생성기 함수에는 화살표 함수(arrow function) 대응 문법이 없습니다. 또한 function과 *는 별개의 토큰이므로 공백이나 줄바꿈으로 분리할 수 있습니다.
설명 (Description)
function* 선언은 GeneratorFunction 객체를 만듭니다. 생성기 함수가 호출될 때마다 새로운 Generator 객체를 반환하며, 이 객체는 iterator protocol(반복자 프로토콜)을 따릅니다. 생성기 함수의 실행은 원래 함수 본문의 가장 처음부터 시작해 특정 지점에서 일시 중단됩니다. 생성기 함수는 여러 번 호출되어 동시에 여러 생성기를 만들 수 있으며, 각 생성기는 생성기 함수의 자체 실행 컨텍스트를 유지하고 독립적으로 진행(step)할 수 있습니다.
생성기는 양방향 제어 흐름을 허용합니다. 제어 흐름은 양쪽이 원하는 만큼 생성기 함수(callee)와 그 호출자(caller) 사이를 전달할 수 있습니다. 제어 흐름은 호출자가 생성기의 next(), throw(), return() 메서드를 호출하여 callee 쪽으로 갈 수 있습니다. 제어 흐름은 callee가 return이나 throw, 또는 모든 문을 실행하며 정상적으로 함수를 종료하거나, yield와 yield* 표현식을 사용하여 caller 쪽으로 갈 수 있습니다.
생성기의 next() 메서드가 호출되면 생성기 함수 본문은 다음 중 하나에 도달할 때까지 실행됩니다.
- yield 표현식: 이 경우
next()메서드는 내보낸 값을 담은value속성과 항상false인done속성을 가진 객체를 반환합니다. 다음에next()를 호출하면 해당 yield 표현식은next()에 전달된 값으로 평가됩니다. - yield* — 다른 반복자에 위임: 이 경우 이 호출과 이후 생성기의
next()호출은 위임된 반복자가 끝날 때까지 위임된 반복자의next()를 호출하는 것과 같습니다. - return 문(try...catch...finally에 가로채이지 않는) 또는 제어 흐름의 끝(암묵적으로
undefined반환): 이 경우 생성기는 완료되며,next()는 반환 값을 담은value속성과 항상true인done속성을 가진 객체를 반환합니다. 이후의next()호출은 효과가 없고 항상{ value: undefined, done: true }를 반환합니다. - 함수 내부에서 발생한 오류(throw 문이나 처리되지 않은 예외):
next()메서드가 그 오류를 던지고 생성기가 완료됩니다. 이후의next()호출은 효과가 없고 항상{ value: undefined, done: true }를 반환합니다.
생성기의 throw() 메서드가 호출되면, 현재 일시 중단된 위치에 throw 문이 삽입된 것처럼 동작합니다. 마찬가지로 생성기의 return() 메서드가 호출되면 현재 일시 중단된 위치에 return 문이 삽입된 것처럼 동작합니다. 두 메서드 모두 보통 생성기를 완료시키지만, 생성기 함수가 try...catch...finally로 완료를 잡으면 예외입니다.
생성기는 한때 비동기 프로그래밍의 패러다임이었으며, 제어의 역전(Inversion of Control)을 달성하여 콜백 지옥(Callback Hell)을 피하는 데 사용됐습니다. 오늘날 이 용도는 더 단순한 async 함수 모델과 Promise 객체로 해결됩니다. 그러나 생성기는 여전히 반복자를 간단하게 정의하는 것 등 많은 다른 작업에 유용합니다.
function* 선언은 함수 선언과 비슷하게 동작합니다 — 자신의 스코프 최상단으로 호이스팅되어 스코프 어디에서든 호출할 수 있고, 특정 컨텍스트에서만 재선언할 수 있습니다.
예제 (Examples)
기본 예제. 무한 ID 생성기입니다.
function* idMaker() {
let index = 0;
while (true) { yield index++; }
}
const gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
yield* 예제. yield*로 다른 생성기에 위임합니다.
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i);
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
생성기에 인자 전달하기. next()에 전달한 값이 yield 표현식의 값이 됩니다.
function* logGenerator() {
console.log(0);
console.log(1, yield);
console.log(2, yield);
console.log(3, yield);
}
const gen = logGenerator();
gen.next(); // 0
gen.next("pretzel"); // 1 pretzel
gen.next("california");// 2 california
gen.next("mayonnaise");// 3 mayonnaise
생성기에서의 return 문.
function* yieldAndReturn() {
yield "Y";
return "R";
yield "unreachable";
}
const gen = yieldAndReturn();
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }
객체 속성 / 메서드 / 계산된 속성으로서의 생성기.
const someObj = {
*generator() { yield "a"; yield "b"; },
};
class Foo {
*generator() { yield 1; yield 2; yield 3; }
*[Symbol.iterator]() { yield 1; yield 2; }
}
const SomeObj = {
*[Symbol.iterator]() { yield "a"; yield "b"; },
};
console.log(Array.from(new Foo())); // [ 1, 2 ]
console.log(Array.from(SomeObj)); // [ 'a', 'b' ]
생성기는 생성자로 사용할 수 없습니다. new f()를 하면 "TypeError: f is not a constructor"가 발생합니다.
생성기 예제. for...of와 함께 무한 시퀀스를 생성하고 break로 제어합니다.
function* powers(n) {
for (let current = n; ; current *= n) {
yield current;
}
}
for (const power of powers(2)) {
if (power > 32) { break; }
console.log(power); // 2, 4, 8, 16, 32
}
사양 및 호환성
function* 선언은 ECMAScript® 2027 Language Specification의 # sec-generator-function-definitions 절에 정의되어 있습니다. Baseline "Widely available"로 분류되어 2016년 9월부터 브라우저 전반에서 사용 가능했습니다.
더 알아보기
- function* 표현식
- Generator
- Iterator
- yield
- yield*
- Iterators and generators 가이드