JavaScript 中的生成器函數

JavaScript 中的生成器函數

在本文中,我們將解釋 JavaScript 中的生成器函數。

YouTube Video

JavaScript 中的生成器函數

JavaScript 生成器函數是與普通函數不同的特殊函數,它們可以進行延遲執行暫停和繼續。通過了解生成器函數,您可以高效地處理非同步操作和大型數據的順序處理。在此處,我們將詳細解釋生成器函數的工作原理、使用方法以及實際使用案例。

什麼是生成器函數?

生成器函數使用 function*(帶星號的函數定義) 定義,與普通函數不同,它可以在執行過程中暫停並在保持其狀態的情況下繼續。生成器函數允許 「延遲執行」 並逐步返回結果,從而實現高效的內存管理和順序處理。

語法

1function* generatorFunction() {
2    yield 'First value';
3    yield 'Second value';
4    return 'Done';
5}

通過這種方式,生成器函數可以包含多個 yield 表達式,並使用 yield 暫停其執行。當調用生成器函數時,函數的主體不會立即執行,而是返回一個 生成器對象。您可以對該對象調用 next() 方法,從函數暫停的點繼續執行。

生成器函數的基本用法

接下來,我們來看一個使用生成器函數的基本範例。

 1function* simpleGenerator() {
 2    yield 1;
 3    yield 2;
 4    yield 3;
 5}
 6
 7const gen = simpleGenerator();
 8
 9console.log(gen.next()); // { value: 1, done: false }
10console.log(gen.next()); // { value: 2, done: false }
11console.log(gen.next()); // { value: 3, done: false }
12console.log(gen.next()); // { value: undefined, done: true }

這裡需要注意的一個關鍵點是,生成器每次通過 yield 返回值,並且只要 done 屬性為 false,就表示還有更多的值將返回。最後一次調用 next() 返回 done: true,表示生成器已完成。

使用 for...of 語法

使用 for...of 可以輕鬆取得生成器中每個 yield 的值。

 1function* simpleGenerator() {
 2    yield 1;
 3    yield 2;
 4    yield 3;
 5}
 6
 7const gen = simpleGenerator();
 8
 9for (const value of gen) {
10    console.log(value); // 1, 2, 3
11}
  • for...of 會自動對可迭代物件(包含生成器)呼叫 .next(),並在 done 變為 true 前持續循環。
  • 在迴圈中,value 會依序被賦值為每個 yield 的值。
  • 使用 for...of 非常簡單,因為你不需要手動呼叫 .next()

yield 關鍵字與暫停值

yield 是一個關鍵字,用於表示生成器函數內的暫停點。當調用 next() 時,yield 右側的值將被返回。此外,yield 支持雙向通信。換句話說,當將一個值作為參數傳遞給 next() 方法時,該值將被發送到生成器函數。

 1function* generatorWithYield() {
 2    const value1 = yield 'First yield';
 3    console.log('Received value:', value1);
 4    const value2 = yield 'Second yield';
 5    console.log('Received value:', value2);
 6}
 7
 8const gen = generatorWithYield();
 9
10console.log(gen.next());        // { value: 'First yield', done: false }
11console.log(gen.next('Apple')); // Received value: Apple
12                                // { value: 'Second yield', done: false }
13console.log(gen.next('Banana'));// Received value: Banana
14                                // { value: undefined, done: true }

在此範例中,調用 next('Apple')'Apple' 傳遞到生成器函數,該值在函數內被使用。

管理生成器狀態

生成器可以維持其執行狀態,這允許以簡潔的方式表示長迴圈或順序處理。以下的範例展示了一個生成器可以無限生成數字。

 1function* infiniteGenerator() {
 2    let i = 0;
 3    while (true) {
 4        yield i++;
 5        if (i > 10) {
 6            break;
 7        }
 8    }
 9}
10
11const gen = infiniteGenerator();
12
13for (const value of gen) {
14    if (value === 3) break;
15    console.log(value); // 0, 1, 2
16}

此生成器使用 while(true) 迴圈不斷生成數字,允許您按需獲取數值。這使對大型數據集的處理更加高效。

生成器函數的應用

生成器函數適合順序執行多個進程。例如,它們在順序處理 API 請求或分割並處理大型文件時非常有用。

1function* apiRequestGenerator() {
2    yield fetch('https://codesparklab.com/json/example1.json');
3    yield fetch('https://codesparklab.com/json/example2.json');
4    yield fetch('https://codesparklab.com/json/example3.json');
5}

因此,可用於異步處理的生成器對高效的順序數據處理非常有幫助。

異步生成器

在 ES2018 中引入的異步生成器允許您通過結合 asyncyield 順序返回異步值。這使得可以結合 await 簡潔地編寫異步處理。

語法

 1async function* asyncGenerator() {
 2    yield await Promise.resolve(1);
 3    yield await Promise.resolve(2);
 4    yield await Promise.resolve(3);
 5}
 6
 7const gen = asyncGenerator();
 8
 9(async () => {
10    for await (const value of gen) {
11        console.log(value); // 1, 2, 3
12    }
13})();

異步生成器可以使用 for await...of 迴圈順序檢索值。此模式在處理異步數據流時特別有用。

實用範例:使用生成器簡化異步處理

生成器函數還可用於簡化異步處理的流程。例如,通過結合 yieldPromise,如以下範例所示,您可以將異步操作編寫得像是同步操作。

 1function* asyncFlow() {
 2    const data1 = yield fetch('https://codesparklab.com/json/example1.json');
 3    console.log(data1);
 4    const data2 = yield fetch('https://codesparklab.com/json/example2.json');
 5    console.log(data2);
 6}
 7
 8const gen = asyncFlow();
 9
10function handleAsync(generator) {
11    const next = (promise) => {
12        promise.then((result) => {
13        const { value, done } = generator.next(result);
14        if (!done) {
15            next(value);
16        }
17        });
18    };
19
20    next(generator.next().value);
21}
22
23handleAsync(gen);

此代碼使用生成器順序地發送 API 請求並處理結果。

總結

生成器函數是 JavaScript 的強大功能之一,具有暫停和恢復函數執行的能力。這使得順序處理、異步處理和高效處理大型數據集成為可能。理解生成器是掌握 JavaScript 高階技術的重要一步。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video