在當代 JavaScript 開發中,我們熟悉了箭頭函式、解構賦值與模板字串等語法糖,但「生成器函式」卻總被視為語法中的冷門角色。不過,它其實具備強大潛力,特別是在控制流程、延遲求值與封裝狀態管理方面,是現代前端工程師的低調救星。

透過這篇文章,我們將系統性解析 Generator(產生器)函式的 特性原理實戰應用模式,幫助你從基礎到進階,掌握這項語法的實用價值。

基礎概念

1.迭代器與可迭代物件(Iterator / Iterable)

  • 定義 next() 的物件即為迭代器。
  • 實作 Symbol.iterator() 的物件為可迭代物件。
const gospelIterator = {
  index: -1,
  next() {
    const gospels = ["Matthew", "Mark", "Luke", "John"];
    this.index++;
    return {
      value: gospels.at(this.index),
      done: this.index + 1 > gospels.length,
    };
  },
};

2.Generator 作為迭代器工廠

function* generateAlphabet() {
  for (let i = 97; i <= 122; i++) {
    yield String.fromCharCode(i);
  }
}

語法特性總覽

  • function* 定義生成器。
  • yield 暫停與傳值。
  • yield* 委派子生成器或其他可迭代物件。

執行機制解析:Generator 如何「延遲」與「記憶」

1.Lazy Evaluation(延遲求值)

  • 每次執行 next() 時才計算。
  • 可用於大量資料或昂貴運算。
function* infiniteCounter() {
  let i = 0;
  while (true) yield i++;
}

2.狀態保留

  • 每次 yield 暫停執行,並保留上下文。

3.控制流程 API

  • next(value):傳值並繼續。
  • return(value):提前結束。
  • throw(err):拋出錯誤中斷。

Generator 應用場景:寫出更清晰的程式邏輯

自訂迭代器:產生器模式的應用

function* idGenerator() {
  let id = 1;
  while (true) yield id++;
}

移動平均數(狀態機應用)

function* calculateMovingAverage(values, windowSize) {
  let start = 0;
  while (start <= values.length - windowSize) {
    yield values.slice(start, start + windowSize)
                .reduce((a, b) => a + b, 0) / windowSize;
    start++;
  }
}

分頁資料流(資料物件延遲產生)

async function* fetchAllPages() {
  let page = 1;
  while (true) {
    const res = await apiRequest(page++);
    if (!res.hasMore) return;
    yield res.items;
  }
}

非同步資料輪詢(Async Generator)

async function* monitorVitals() {
  while (true) {
    yield await requestVitals();
    await delay(1000);
  }
}

控制反轉(Inversion of Control)

for (const value of generator) {
  await new Promise(r => {
    button.addEventListener("click", () => {
      render(value);
      r();
    }, { once: true });
  });
}

進階技巧與模式應用

錯誤處理與 try-catch 配合

function* g() {
  try {
    yield 1;
    yield 2;
  } catch (e) {
    console.log("Caught:", e);
  }
}

yield* 委派機制

function* sub() {
  yield 'a';
  yield 'b';
}
function* main() {
  yield* sub();
  yield 'c';
}

實戰應用:DOM 元素批次產生

function* getElements(tagName) {
  while (true) yield document.createElement(tagName);
}
const [div1, div2] = getElements('div');

Generator 提供一種清晰、延遲、可控的資料生產流程。
適合處理狀態儲存、資料流、非同步控制與大型迭代物件。

來源:
MDN Generator Functions

Async Iteration Proposal

標籤: ,