function*

function* (생성기 함수 선언)

function* 선언은 주어진 이름에 새로운 생성기 함수(generator function) 바인딩을 만듭니다. 생성기 함수는 함수를 빠져나갔다가 나중에 다시 진입할 수 있으며, 재진입 사이에 문맥(변수 바인딩)이 보존됩니다. 원문은 MDN의 JavaScript 참고 문서입니다.

출처: function* - JavaScript | MDN

본문

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, 또는 모든 문을 실행하며 정상적으로 함수를 종료하거나, yieldyield* 표현식을 사용하여 caller 쪽으로 갈 수 있습니다.

생성기의 next() 메서드가 호출되면 생성기 함수 본문은 다음 중 하나에 도달할 때까지 실행됩니다.

  • yield 표현식: 이 경우 next() 메서드는 내보낸 값을 담은 value 속성과 항상 falsedone 속성을 가진 객체를 반환합니다. 다음에 next()를 호출하면 해당 yield 표현식은 next()에 전달된 값으로 평가됩니다.
  • yield* — 다른 반복자에 위임: 이 경우 이 호출과 이후 생성기의 next() 호출은 위임된 반복자가 끝날 때까지 위임된 반복자의 next()를 호출하는 것과 같습니다.
  • return 문(try...catch...finally에 가로채이지 않는) 또는 제어 흐름의 끝(암묵적으로 undefined 반환): 이 경우 생성기는 완료되며, next()는 반환 값을 담은 value 속성과 항상 truedone 속성을 가진 객체를 반환합니다. 이후의 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월부터 브라우저 전반에서 사용 가능했습니다.

더 알아보기