在Node.js中深入理解JavaScript生成器(Generators)
本文详细解析JavaScript中的生成器(Generators)概念及其在Node.js中的应用。通过function*语法、yield关键字及next()方法,生成器实现了代码暂停与恢复。文章探讨了其在异步流程控制、懒加载无限序列及简化复杂函数逻辑方面的优势,并对比了与普通函数的性能差异,帮助开发者掌握这一高级特性。
生成器基础语法与执行机制
生成器(Generators)是JavaScript中一种类似协程(coroutine)的编程风格,指那些可以在执行过程中暂停并在后续时刻恢复执行的函数。在语法上,生成器函数通过function*符号定义,函数内部包含yield和yield*等特征关键字。
function* generatorFn () {
console.log('look ma I was suspended')
}
var generator = generatorFn() // [1]
setTimeout(function () {
generator.next() // [2]
}, 2000)
针对代码中涉及的[1]和[2]标记,具体解释如下:
1. 这是一个以暂停状态开始的生成器。此时,函数体内的代码尚未执行,因此控制台没有任何输出。
2. 只有通过调用其next()方法,生成器才会开始执行,一直运行到遇到下一个yield关键字或return语句为止。此时,控制台才会显示相应的输出结果。
再观察一个具体案例:
生成器返回值与状态属性
function *generator() {
console.log('Start!');
var i = 0;
while (true) {
if (i < 3)
yield i++;
}
}
var gen = generator();
上述代码与第一个示例类似,但在生成器函数中增加了yield关键字。当该函数被调用时,并不会立即执行内部逻辑,而是进入暂停待命状态,因此不会输出“Start”。直到显式调用next()方法后,代码才会继续执行。
var ret = gen.next();
// Start!
console.log(ret);
// {value: 0, done: false}
上述代码中的ret变量代表生成器的执行结果。它包含两个关键属性:
■ value:对应生成器函数中yield语句返回的值。
■ done:这是一个布尔标识,用于表示生成器函数是否已经执行完毕并返回。
继续查看后续代码示例:
console.log(gen.next());
// {value: 1, done: false}
console.log(gen.next());
// {value: 2, done: false}
console.log(gen.next());
// {value: undefined, done: true}
在同步编程场景中,生成器并没有太多特殊之处,但它特别适合用于处理异步编程问题。
生成器主要具备两个显著特点:
1. 允许程序从函数中跳出,交由外部代码决定何时重新跳回该函数继续执行。 2. 能够有效进行异步控制。
参考以下异步执行的代码示例:
生成器在异步编程中的应用
var gen = generator();
console.log(gen.next().value);
setTimeout(function() {
console.log(gen.next().value);
console.log('第一步');
}, 1000);
console.log('第二步');
其输出结果为:
0 第二步 1 第一步
这意味着,代码不会在setTimeout处等待计时结束,而是直接继续执行“第二步”,避免了在setTimeout处的阻塞等待。
再看另外一段代码示例:
function* channel () {
var name = yield 'hello, what is your name?' // [1]
return 'well hi there ' + name
}
var gen = channel()
console.log(gen.next().value) // hello, what is your name? [2]
console.log(gen.next('billy')) // well hi there billy [3]
在遍历操作中也可以使用*号:
function* iter () {
for (var i = 0; i < 10; i++) yield i
}
for (var val of iter()) {
console.log(val) // outputs 1?—?9
}
常见的误解
既然可以暂停函数执行,是否可以让它们并行执行呢?答案是否定的。因为JavaScript是单线程环境,如果你旨在提升性能,生成器并不是合适的选择。
例如下面代码分别执行斐波那契数列的计算:
性能对比与懒加载特性
function fib (n) {
var current = 0, next = 1, swap
for (var i = 0; i < n; i++) {
swap = current, current = next
next = swap + next
}
return current
}
function* fibGen (n) {
var current = 0, next = 1, swap
for (var i = 0; i < n; i++) {
swap = current, current = next
next = swap + next
yield current
}
}
性能测试结果如下:(数值越高表示性能越好)
results: regular 1263899 generator 37541
生成器的亮点所在
生成器能够简化JavaScript中函数的复杂性。
懒加载(Lazy Evaluation)
虽然可以使用JS闭包实现懒加载,但使用yield会大大简化代码。通过暂停和恢复机制,我们可以在需要时获取数值。例如上面的fibGen函数,可以在我们需要时按需拉取新值:
var fibIter = fibGen(20)
var next = fibIter.next()
console.log(next.value)
setTimeout(function () {
var next = fibIter.next()
console.log(next.value)
},2000)
当然还使用for循环:依然是懒赋值
for (var n of fibGen(20) {
console.log(n)
}
无限序列
由于支持懒加载,我们可以借鉴Haskell中的技巧,实现类似无限序列(infinite sequences)的效果。这里可以yield出一个无限序列的数值。
无限序列与异步流程控制
function* fibGen () {
var current = 0, next = 1, swap
while (true) {
swap = current, current = next
next = swap + next
yield current
}
}
我们来看一个斐波那契数列流的懒加载示例,要求返回5000之后的第一个斐波那契数:
for (var num of fibGen()) {
if (num > 5000) break
}
console.log(num) // 6765
异步流程控制
使用生成器实现异步流程控制,最常见的是各种Promise库。那么它是如何工作的呢?
在Node.js领域,所有操作都与回调相关,这是底层的异步功能。我们可以使用生成器建立一个通信通道,从而以同步编程的风格来编写异步代码。
使用Genify框架简化异步代码
run(function* () {
console.log("Starting")
var file = yield readFile("./async.js") // [1]
console.log(file.toString())
})
注释1表示程序会在等待async.js返回结果后再继续执行后续代码。
genify是一个将生成器引入常规编程环境的框架,使用方法如下:
通过npm install genify进行安装,具体代码如下:
var Q = require('q');
var fs = require('fs');
var genify = require('genify');
// wrap your object into genify function
var object = genify({
concatFiles: function * (file1, file2, outFile) {
file1 = yield Q.nfcall(fs.readFile, file1);
file2 = yield Q.nfcall(fs.readFile, file2);
var concated = file1 + file2;
yield Q.nfcall(fs.writeFile, outFile, concated);
return concated;
}
});
// concatFiles是一个generator函数,它使用generator强大能力。
object.concatFiles('./somefile1.txt', './somefile2.txt', './concated.txt').then(function (res) {
// do something with result
}, function (err) {
// do something with error
});
以上这篇在Node.js中使用Javascript Generators详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。
Java和JavaScript的应用场景分工:后端企业级开发与Web前端交互
2026-11-05 15:57:34
除了浏览器和Node.js,还有哪些轻量级Js脚本运行工具?
2026-11-04 17:38:24
无需后端:用JavaScript实现简单网站的本地数据存储
2026-11-03 23:03:30
Node.js 下载慢或官网打不开怎么办
2026-11-03 18:19:37
JS解析大体积Excel文件内存溢出怎么办
2026-11-03 18:19:37
