ZetCode

JavaScript yield 关键字

最后修改于 2025 年 4 月 16 日

在本文中,我们将展示如何在 JavaScript 中使用带有 yield 关键字的生成器函数。

yield 关键字

yield 关键字用于在生成器函数中暂停执行并返回值。当生成器恢复时,执行将从暂停的位置继续。这允许迭代值的产生。

生成器函数使用 function* 语法声明。它们返回一个可迭代的生成器对象。 yield 关键字是使生成器与常规函数不同的关键。

生成器在执行之间保持其状态。这使得复杂的迭代和惰性求值成为可能。它们对于有效处理大型数据集或无限序列非常有用。

基本生成器函数

此示例演示了 yield 在生成器中的最简单用法。

main.js
function* simpleGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = simpleGenerator();

console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

生成器按顺序产生三个值。每次调用 next() 都会恢复执行,直到下一个 yield。生成器会记住它在调用之间的状态。

$ node main.js
1
2
3

具有无限序列的生成器

生成器可以生成无限序列而无需消耗内存。

main.js
function* infiniteSequence() {
    let i = 0;
    while (true) {
        yield i++;
    }
}

const gen = infiniteSequence();

console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

此生成器生成一个无限的数字序列。它不会一次将所有值存储在内存中。每个值都是在需要时通过调用 next() 生成的。

$ node main.js
0
1
2

将值传递给 yield

值可以通过 next() 方法传递到生成器中。

main.js
function* generatorWithInput() {
    const name = yield 'What is your name?';
    const age = yield `Hello ${name}, how old are you?`;
    yield `${name} is ${age} years old`;
}

const gen = generatorWithInput();

console.log(gen.next().value);
console.log(gen.next('John').value);
console.log(gen.next(30).value);

生成器首先产生一个问题。传递给 next() 的值成为 yield 表达式的结果。这使得生成器和调用者之间能够进行双向通信。

$ node main.js
What is your name?
Hello John, how old are you?
John is 30 years old

使用 yield* 进行委托

yield* 表达式委托给另一个生成器。

main.js
function* innerGenerator() {
    yield 'a';
    yield 'b';
}

function* outerGenerator() {
    yield 1;
    yield* innerGenerator();
    yield 2;
}

const gen = outerGenerator();

for (const val of gen) {
    console.log(val);
}

yield* 表达式产生来自 innerGenerator 的所有值。这类似于展开内部生成器的值。它提供了一种组合生成器的方法。

$ node main.js
1
a
b
2

生成器中的早期返回

生成器可以使用 return 返回最终值。

main.js
function* generatorWithReturn() {
    yield 'First';
    yield 'Second';
    return 'Done';
    yield 'This will not execute';
}

const gen = generatorWithReturn();

console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

return 语句结束生成器。后续的 next() 调用返回 {value: undefined, done: true}。返回值出现在 done 变为 true 之前的最后一个 next() 调用中。

$ node main.js
{ value: 'First', done: false }
{ value: 'Second', done: false }
{ value: 'Done', done: true }
{ value: undefined, done: true }

生成器中的错误处理

生成器可以使用 try-catch 块处理错误。

main.js
function* errorHandlingGenerator() {
    try {
        yield 'Start';
        throw new Error('Something went wrong');
        yield 'This will not execute';
    } catch (err) {
        yield `Caught error: ${err.message}`;
    }
    yield 'End';
}

const gen = errorHandlingGenerator();

console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

在生成器内部抛出的错误可以在生成器内部被捕获。生成器在 catch 块之后继续执行。这允许在异步操作中进行可靠的错误处理。

$ node main.js
Start
Caught error: Something went wrong
End

实际用例:分页

生成器对于实现分页很有用。

main.js
function* paginate(items, pageSize) {
    for (let i = 0; i < items.length; i += pageSize) {
        yield items.slice(i, i + pageSize);
    }
}

const items = Array.from({length: 10}, (_, i) => i + 1);
const pages = paginate(items, 3);

for (const page of pages) {
    console.log('Page:', page);
}

此生成器产生来自数组的项目页面。每次调用 next() 都会返回下一页。这种模式对于大型数据集来说是内存效率的。

$ node main.js
Page: [ 1, 2, 3 ]
Page: [ 4, 5, 6 ]
Page: [ 7, 8, 9 ]
Page: [ 10 ]

来源

yield - 语言参考

在本文中,我们已经演示了如何在 JavaScript 的生成器函数中使用 yield 关键字。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

查看 所有 JavaScript 教程。