在Node.js中深入理解JavaScript生成器(Generators)

作者:终端旅客

时间:2026-10-30

浏览:579

本文详细解析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 &lt; 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 &lt; 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 &lt; 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 &gt; 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详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章