跳到主要内容

JavaScript 生成器

提示
  1. 创建和使用 JavaScript 生成器:通过 function* 符号定义生成器函数,使用 yield 关键字暂停函数执行,并通过 next() 方法恢复执行。
  2. 生成器的多重 yield 处理yield 表达式返回值但不终止程序,允许从上次暂停的位置继续执行代码。
  3. 生成器用于实现迭代器:生成器提供了实现迭代器的简便方式,可通过 for...of 循环进行迭代,并支持 next()return()throw() 方法。

在 JavaScript 中,生成器为 函数迭代器 的工作方式提供了一种新方法。

使用生成器,

  • 你可以在函数内的任何地方停止函数的执行
  • 并从停止的位置继续执行代码

创建 JavaScript 生成器

要创建生成器,你需要首先用 function* 符号定义一个生成器函数。生成器函数的对象称为生成器。

// 定义一个生成器函数
function* generator_function() {
... .. ...
}

// 创建一个生成器
const generator_obj = generator_function();

注意:生成器函数由 * 表示。你可以使用 function* generatorFunc() {...}function *generatorFunc(){...} 来创建它们。

使用 yield 暂停执行

如上所述,你可以暂停执行生成器函数,而不需要执行整个函数体。为此,我们使用 yield 关键字。例如,

// 生成器函数
function* generatorFunc() {
console.log("1. 第一个 yield 之前的代码");
yield 100;

console.log("2. 第二个 yield 之前的代码");
yield 200;
}

// 返回生成器对象
const generator = generatorFunc();

console.log(generator.next());

输出

1. 第一个 yield 之前的代码
{value: 100, done: false}

这里,

  • 创建了一个名为 generator 的生成器对象。
  • 当调用 generator.next() 时,代码会执行到第一个 yield 并在遇到 yield 时返回值并暂停生成器函数。

注意:在使用之前,你需要将生成器对象赋值给一个变量。

多个 yield 语句的工作原理

yield 表达式返回一个值。然而,与 return 语句不同,它不会终止程序。这就是为什么你可以从最后一个 yield 的位置继续执行代码。例如,

function* generatorFunc() {
console.log("1. 第一个 yield 之前的代码");
yield 100;

console.log("2. 第二个 yield 之前的代码");
yield 200;

console.log("3. 第二个 yield 之后的代码");
}

const generator = generatorFunc();

console.log(generator.next());
console.log(generator.next());
console.log(generator.next());

输出

1. 第一个 yield 之前的代码
{value: 100, done: false}
2. 第二个 yield 之前的代码
{value: 200, done: false}
{value: undefined, done: true}

这个程序的工作原理如下。

  • 第一个 generator.next() 语句执行直到第一个 yield 语句并暂停程序的执行。
  • 第二个 generator.next() 从暂停的位置开始程序。
  • 当所有元素被访问后,它返回 {value: undefined, done: true}

JavaScript 中生成器函数的工作原理

向生成器函数传递参数

你也可以向生成器函数传递参数。例如,

// 生成器函数
function* generatorFunc() {
// 在第一个 next() 时返回 'hello'
let x = yield "hello";

// 在第二个 next() 时返回传递的参数
console.log(x);
console.log("一些代码");

// 在第二个 next() 时返回 5
yield 5;
}

const generator = generatorFunc();

console.log(generator.next());
console.log(generator.next(6));
console.log(generator.next());

输出

{value: "hello", done: false}
6
一些代码
{value: 5, done: false}
{value: undefined, done: true}

在上述程序中,

  • 第一个 generator.next() 返回 yield 的值(在这种情况下是 'hello')。然而,这个值并没有分配给 let x = yield 'hello'; 中的变量 x。
{value: "hello", done: false}
  • 当遇到 generator.next(6) 时,代码再次从 let x = yield 'hello'; 开始执行,参数 6 被赋值给 x。然后执行余下的代码直到第二个 yield
6
some code
{value: 5, done: false}
  • 当执行第三个 next() 时,程序返回 {value: undefined, done: true}。这是因为没有其他的 yield 语句。
{value: undefined, done: true}

生成器用于实现迭代器

生成器提供了一种更简单的方式来实现迭代器(iterators)

如果你想手动实现一个迭代器,你需要创建一个带有 next() 方法的迭代器并保存状态。例如,

// 创建可迭代对象
const iterableObj = {
// 迭代器方法
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step === 1) {
return { value: "1", done: false };
} else if (step === 2) {
return { value: "2", done: false };
} else if (step === 3) {
return { value: "3", done: false };
}
return { value: "", done: true };
},
};
},
};

for (const i of iterableObj) {
console.log(i);
}

输出

1;
2;
3;

由于生成器是可迭代的,你可以用更简单的方式实现迭代器。然后你可以使用 for...of 循环遍历生成器。例如,

// 生成器函数
function* generatorFunc() {
yield 1;
yield 2;
yield 3;
}

const obj = generatorFunc();

// 遍历生成器
for (let value of obj) {
console.log(value);
}

生成器方法

方法描述
next()返回 yield 的值
return()返回值并终止生成器
throw()抛出错误并终止生成器

JavaScript 中的 return 与 yield 关键字

return 关键字yield 关键字
返回值并终止函数。返回值并暂停函数,但不终止函数。
在普通函数和生成器函数中都可用。仅在生成器函数中可用。

带 return 的 JavaScript 生成器函数

你可以在生成器函数中使用 return 语句。return 语句返回一个值并终止函数(类似于普通函数)。例如,

// 生成器函数
function* generatorFunc() {
yield 100;

return 123;

console.log("2. some code before second yield");
yield 200;
}

// 返回生成器对象
const generator = generatorFunc();

console.log(generator.next());
console.log(generator.next());
console.log(generator.next());

输出

{value: 100, done: false}
{value: 123, done: true}
{value: undefined, done: true}

在上述程序中,当遇到 return 语句时,它返回值并且 done 属性变为 true,函数终止。因此,return 语句之后的 next() 方法不会返回任何内容。

注意:你也可以使用 return() 方法替代 return 语句,就像上述代码中的 generator.return(123);

JavaScript 生成器的 Throw 方法

你可以使用 throw() 方法显式地在生成器函数上抛出错误。throw() 方法的使用会抛出错误并终止函数。例如,

// 生成器函数
function* generatorFunc() {
yield 100;
yield 200;
}

// 返回生成器对象
const generator = generatorFunc();

console.log(generator.next());

// 抛出错误
// 终止生成器
console.log(generator.throw(new Error("Error occurred.")));
console.log(generator.next());

输出

{value: 1, done: false}
Error: Error

occurred.

生成器的用途

  • 生成器让我们在编写异步任务时能写出更清晰的代码。
  • 生成器提供了实现迭代器的更简单方式。
  • 生成器仅在需要时执行其代码。
  • 生成器对内存使用高效。

生成器在 ES6 中被引入。一些浏览器可能不支持生成器的使用。要了解更多,请访问 JavaScript 生成器支持。$$$