JavaScript生成器函数
在JavaScript中,生成器函数是一个可以暂停和稍后恢复的函数,允许它随着时间的推移产生一系列值,而不是一次计算它们并在单个数组中返回它们。
这是一个生成数字序列的生成器函数的简单示例:
function* numbers() {
yield 1;
yield 2;
yield 3;
}
const generator = numbers();
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
console.log(generator.next().value); // 3
要创建生成器函数,您可以使用function*语法,然后使用yield关键字生成一个值。每次迭代生成器(例如,使用for循环)时,它将执行代码,直到下一个yield语句,然后暂停。当生成器再次迭代时,它将从中断的地方继续执行,直到下一个yield语句。
生成器函数对于生成大型数据序列非常有用,因为它们允许您一次生成一个值,而不是一次生成所有值。这可以更节省内存,也可以允许您在生成值时处理它们,而不必等待整个序列被计算后才能开始处理它。
以下是JavaScript中生成器函数的一些用例示例:
- 迭代大型数据集:如果您有一个大型数据集,您希望一次处理一个项目,则可以使用生成器函数在处理数据集中的每个项目时产生它,而不是一次将整个数据集读入内存。
- 实现无限序列:生成器函数可用于创建无限序列,例如无限范围的数字或无限随机数流。
- 实现异步代码:生成器函数可以用来以看起来同步的风格编写异步代码,使用
yield关键字暂停生成器函数,使用next()方法恢复它。这可以使异步代码更容易读写。 - 实现惰性求值:生成器函数可用于实现惰性求值,即在需要时才计算序列中的下一个值。这对于优化某些类型算法的性能很有用。
这是一个生成无限随机数序列的生成器函数示例:
function* randomNumbers() {
while (true) {
yield Math.random();
}
}
const generator = randomNumbers();
console.log(generator.next().value); // 0.123456789
console.log(generator.next().value); // 0.987654321
console.log(generator.next().value); // 0.654321987
将参数传递给生成器函数
要在JavaScript中将参数传递给生成器函数,您可以使用next()方法并将参数作为参数传递给next()方法。
这是一个接受两个参数的生成器函数示例:
function* add(x, y) {
const result = x + y;
yield result;
}
const generator = add(10, 20);
console.log(generator.next().value); // 30
要在每次迭代时将不同的参数传递给生成器函数,您可以使用循环并在循环内使用所需的参数调用next()方法。
这是一个生成器函数的棘手示例,它接受两个参数并生成将它们相加的结果以获取一系列输入值:
function* add(x, y) {
while (true) {
const result = x + y;
x = yield result;
y = yield result;
}
}
const generator = add(10, 20);
for (let i = 1; i <= 5; i++) {
console.log(generator.next(i * 10, i * 20).value);
}
// Output:
// 30
// 30
// 50
// 50
// 90
在这个例子中,生成器函数有一个无限循环,并使用yield关键字暂停生成器并返回一个值。next()方法在循环内部调用,x和y的新值作为参数,这会导致生成器函数恢复执行并计算下一个结果。
上面的例子很棘手,因为我们向next()方法传递了两个参数,这可能会让您感到困惑。当谈到生成函数和传递参数时,我们需要在这里了解一些事情:
- 生成器函数可以退出,稍后重新输入。
- 变量绑定将保存在所有重新入口中。
- 调用生成器函数不会立即执行该函数。
- 当我们调用
next()方法时,函数的主体会被执行,直到执行达到yield。 -
next()方法只接受一个参数。所有其他参数都将被忽略。 - 我们使用
next()方法参数传递的值将替换暂停执行的yield表达式。
现在有了上面所有这些理解,让我们浏览一下代码。
在迭代1
- x是10,y是20
- 它在x=yield result处停止,控制台中的结果为30。
在迭代2
- 这里我们调用
next()方法,而不是add()方法 - 继续执行
- x是20,因为参数是i*10(i是2)
- y保持旧值为20(next方法的第二个参数被忽略)
- 它在y=yield result时停止;控制台中的结果又是30(携带旧的求和)
在迭代3
- 继续执行
- x是20并继续
- y是30并且继续
- 它在x=yield result 时停止
- 所以结果是50。控制台打印50
在迭代4
- 继续执行
- 现在next()将替换x值,因为它被停止了x。所以新的x值是4*10,也就是40。
- y仍然是30
- 但是没有计算新的结果。所以打印旧的结果50
在迭代5
- 继续执行
- x仍然是40,但现在y将从next变为5*10的值,即50
- 新的结果表达式执行并产生90
请记住:在所有这些迭代中,next()方法的第二个参数没有任何作用。它被忽略了。
Generators and Promises
在JavaScript中,生成器(generator)函数可以与Promises结合使用,以同步样式处理异步代码。
以下是使用生成器函数和Promise从远程API异步获取数据的示例:
function* fetchData() {
const response = yield fetch('https://example.com/api/data');
const data = yield response.json();
return data;
}
function runGenerator(generator) {
const iterator = generator();
function iterate(iteration) {
if (iteration.done) return iteration.value;
const promise = iteration.value;
return promise.then(result => iterate(iterator.next(result)));
}
return iterate(iterator.next());
}
const dataPromise = runGenerator(fetchData);
dataPromise.then(data => {
console.log(data);
});
在这个例子中,fetchData生成器函数使用yield关键字暂停执行,并返回一个Promise,表示从远程API获取数据的异步操作。runGenerator函数是一个辅助函数,它接受一个生成器函数并运行它,遍历生成器,并用then()方法解析生成器函数返回的Promises。
这允许您以看起来同步的风格编写异步代码,使用yield关键字暂停generator(生成器)函数,使用next()方法恢复它。这可以使异步代码更易于读写。