这里记录工作中遇到的技术点,以及自己对生活的一些思考,周三或周五发布。
封面图
Node中的streams流
streams流是Node中的最好的特性之一。它在我们的开发过程当中可以帮助我们做很多事情。比如通过流的方式梳理大量数据,或者帮我们分离应用程序。
和streams流相关的内容有哪些呢?大致有这么几点:
- 处理大量数据
- 使用管道方法
- 转换流
- 读写流
- 解耦I/O
读写流
可读流可以帮助我们做一些非常有意思的事情。比如可以读取不适合内存的数据,以及可以用来处理数据量特别大的数据。而可写流则可以用来操作输出,比如可以用来控制硬件,或者将数据推送过去,进行批量处理。
这里我们需要用到两个模块儿,from2 和 to2。
还是举个例子:
const from = require('from2')
const to = require('to2')
const rs = from(() => {
rs.push(Buffer.from('Hello, my name is terrence'))
rs.push(null)
})
rs.on('data', (data) => {
console.log(data.toString())
})
我们可以看到终端打印出来的信息。
但是这个结果是通过监听data事件后打印的。我们可以用可写流进行实现:
const from = require('from2')
const to = require('to2')
const rs = from(() => {
rs.push(Buffer.from('Hello, my name is terrence'))
rs.push(null)
})
//rs.on('data', (data) => {
// console.log(data.toString())
//})
const ws = to((data, enc, cb) => {
console.log('你写的是:' + data.toString())
cb()
})
rs.pipe(ws)
然后重新执行,我们仍旧可以看到结果:
这是为什么
from2模块基于stream.Readable的构造器进行了一层包装,并且为我们创建了一个流。而且它还有一个好处是,它有一个destory方法可以自由的释放流资源,并且具有异步推送的功能。
同时,和之前提到过的through2模块儿类似。from2和to2模块都具有对象模式,可以很方便的创建对象流。
当我们通过pipe将rs可读流送入ws可写流的时候,rs通过push方法推入了我们想要输出的字符串。然后可写流接收到数据,并且cb()方法指明下一段数据已经standby。rs推入null,表明这个流已经结束了,通过管道,ws可写流也触发结束事件。
大体是这么个过程。
核心模块的读写流
如果我们想要实现自己的读写流,我们可能需要基于stream.Readable构造器进行扩展。
这个基类会调用一个_read方法。关键的地方就在于我们如何去实现这个_read方法。我们也可以在options参数对象中提供一个read属性,当然它是个方法,这个方法将被添加为实例的_read方法。
不管我们什么时候调用read方法,steam总是会需要我们提供一些数据供stream进行消费。我们可以通过push方法给他推入一些数据。
举个例子:
const { Readable, Writable } = require('readable-stream')
const rs = Readable({
read: () => {
rs.push(Buffer.from('terrence'))
rs.push(null)
},
})
const ws = Writable({
write: (data, enc, cb) => {
console.log(`很好,知道你是`, data.toString())
cb()
},
})
rs.pipe(ws)
这样,我们仍然可以实现上面的效果。
核心可读流流量控制问题
可读流上的_read方法不接受回调。由于stream流通常包含不止一个数据缓冲区,因此流需要多次调用_read方法。
这就需要等待调用push,然后在流的内部缓冲区有可用空间时再次调用_read方法。如果我们想以异步方式多次调用push,这就会出现一个问题。
比如:
const { Readable, Writable } = require('readable-stream')
const rs = Readable({
read: () => {
setTimeout(() => {
rs.push(Buffer.from('terrence'))
setTimeout(() => {
rs.push(Buffer.from('apple'))
}, 500)
}, 1000)
// rs.push(null)
},
})
rs.on('data',(data)=>{
console.log('姓名:',data.toString())
})
上面的代码,我们希望能够交替打印terrence和apple。但是实际情况如下:
但是,通过之前我们提到的from模块,我们可以解决这个问题
const from = require('from2')
const rs = from((size, cb) => {
setTimeout(() => {
rs.push(Buffer.from('terrence'))
setTimeout(() => {
rs.push(Buffer.from('apple'))
cb()
}, 500)
}, 1000)
})
rs.on('data', (data) => {
console.log('姓名:', data.toString())
})
再次打印,会发现一切正常。
最后
- 公众号《JavaScript高级程序设计》
- 公众号内回复”vue-router“ 或 ”router“即可收到 VueRouter源码分析的文档。
- 回复”vuex“ 或 ”Vuex“即可收到 Vuex 源码分析的文档。
全文完,如果喜欢。
请“点赞”和"在看"吧,最好也加个"关注",或者分享到朋友圈。