Generator是一个比较老的特性了,但是这个特性貌似并不受广大编程爱好者的喜爱,包括我也只是一知半解,知道怎么去写它,知道它的原理,但是对它的使用场景积累的并不是很深。
今天本来想看一看quic的实现方案,于是就看了quic这个仓库的源码,我在它的example文件夹内发现了一段测试代码,看着写的还不错,是针对quic的服务测试客户端代码
thunk(function * () {
yield cli.connect(2345)
yield cli.ping()
const stream = cli.request()
stream
.on('error', (err) => ilog.error(Object.assign(err, { class: 'client stream error' })))
.on('data', (data) => {
ilog.info(`client stream ${stream.id} data: ${data.toString()}`)
})
.on('end', () => {
ilog.info(`client stream ${stream.id} ended`)
cli.close()
})
.on('finish', () => {
ilog.info(`client stream ${stream.id} finished`)
})
yield (done) => stream.write('hello, QUIC', done)
let i = 0
while (i <= 99) {
yield thunk.delay(100)
yield (done) => stream.write(`${i++}`, done)
}
stream.end()
yield (done) => cli.once('close', done)
yield server.close()
})(ilog.error)
上面的六个yield代码完整地封装了一个client创建连接到销毁的全过程,全程交给thunk托管,这个回调一共执行了 100次 每次间隔100ms。
下面是简要的执行流程
- 第一次执行,创建链接并发送一次请求
- 发送请求...99
- 第100次执行,发送请求后关闭连接
如果是我之前组织这样的代码,我可能会定义一个类去封装这些,但是如果像上面这种写法,在不是很复杂的场景下是非常适用的。
扩展一下,是不是所有这种需要开启和关闭的通道类函数都可以使用这种方式封装呢,比如一个读文件流,每次只读取一行,那么读取到最后一行则自动关闭对该文件的通道。这么想,这个Generator的使用是大有作为的。