Skip to content

理解 JavaScript Generator 函式與 yield 運作原理

此文章是 FrontendMaster 上的 Advanced Web Development Quiz 課程筆記

問題

When does In log: My input! get logged?

Select the correct answer.

function* generatorFunction() {
  const result = yield "My input!";
  console.log("In log:", result);
  return "All done!";
}
const it = generatorFunction();
[1] it.next()
[2] it.next("My input!"), it.next()
[3] it.next(), it.next("My input!")
[4] it.next(), it.next()

JavaScript Generator

Generator 是個特別的函式,相較於一般函式執行後只能回傳一次值,Generator 透過 yield 可以回傳多個值。建立 Generator 的方式就是加上 *,如:

javascript
function* generatorFunction() {
    // ...
}

Generator 函式被呼叫的時候,function body 不會直接被執行,而是回傳 generator object

javascript
function* generatorFunction() {
    // ...
}

console.log(generatorFunction()); // Object [Generator] {}

接下來可以透過呼叫 next 方法來觸發執行,此時 function 會開始執行,直到完成執行出現 yield 的那一行並停止且回傳一個物件 (例如:{value: 1, done: false} )。 接續執行 next 之後就會繼續從上一次停止的位置執行。如果執行完畢或是遇到 return 此時回傳的物件中的 done 就會變成 true

javascript
function* generatorFunction() {
    yield 1;
    yield 2;
    return 3;
}

const generator = generatorFunction();
console.log(generator.next()); // {value: 1, done: false}
console.log(generator.next()); // {value: 2, done: false}
console.log(generator.next()); // {value: 3, done: true}

透過 next 傳值

當我們在 next() 方法中帶入參數時,這個參數會成為上一個 yield 表達式的回傳值。

javascript
function* generatorFunction() {
    const result = yield 'hello';
    console.log('Log: ' + result);
    yield 2;
}

const generator = generatorFunction();
console.log(generator.next());
// {value: "hello", done: false}

console.log(generator.next('YO!'));
// "Log: YO!"
// {value: 2, done: false}

注意:第一次呼叫 next() 時傳入的參數會被忽略,因為此時還沒有對應的 yield 表達式來接收這個值。

搭配 try...catch...finally

generator 必須要有執行過 body 內的程式 (執行過 next),才執行 returnthrow,這樣子才會去執行 catchfinally 內的程式

我們也可以透過 generator.return 直接讓這個 generator 直接結束,這可以跟 try...finally 一起使用

javascript
function* generatorFunction() {
    try {
        yield 1;
        yield 2;
    } finally {
        yield 3;
        console.log('finally');
    }
}

const generator = generatorFunction();

console.log(generator.next());
// { value: 1, done: false }

console.log(generator.return());
// { value: 3, done: false }

console.log(generator.next());
// finally
// { value: undefined, done: false }

使用 try...catch 搭配 generator.throw,就可以做到更細緻的錯誤控制

javascript
function* generatorFunction() {
    try {
        yield 1;
        yield 2;
    } catch (e) {
        alert(e);
    }
}

const generator = generatorFunction();

console.log(generator.next());
// { value: 1, done: false }

console.log(generator.throw(new Error('Something went wrong!')));
// alert !
// { value: undefined, done: false }

generator 可以被迭代

我們可以透過 for...of 來對 generator 跑迴圈。

javascript
function* generatorFunction() {
    yield 1;
    yield 2;
    return 3;
}

const generator = generatorFunction();
for (value of generator) {
    console.log(value); // 1 , 2
}

因為 for...of 只會抓 yield 的值,所以最後不會印出 3,需要這個值的話應該要改成使用 yield

實例

我從來沒有實際使用過這個功能,不過似乎可以用來生成 runtime 期間的 id

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

const generator = idGenerator();

console.log(generator.next()); // 1
console.log(generator.next()); // 2
console.log(generator.next()); // 3

答案

[3] it.next(), it.next("My input!")

參考

最後更新時間:

0 %
MIT Licensed | Copyright © 2025-present Wen-Hsiu's Blog