本文详细的介绍了node Transform ,分享给大家,希望此文章对各位有所帮助。
Transform流特性
在开发中直接接触Transform流的情况不是很多,往往是使用相对成熟的模块或者封装的API来完成流的处理,最为特殊的莫过于through2模块和gulp流操作。那么,Transform流到底有什么特点呢?
从名称上说,Transform意为处理,类似于生产流水线上的每一道工序,每道工序针对到来的产品作相应的处理;从结构上看,Transform是一个双工流,通俗的解释它既可以作为可读流,也可作为可写流。但是,node却对Transform流针对其特性做了更为特殊的定制,使Transform不是单纯的Duplex流。
Transform流由于包含了Readable和Writeable特性,因此Transform在实际使用中有着多种方式:它既可以只作为消费者消费数据,也可同时作为生产者和消费者完成数据中间处理。下面将逐渐深入内部阐述Transform的运行机理及使用技巧。
Transform内部架构
上图表示一个Transform实例的组成部分:Readable部分缓冲(数组)、内部_read函数、Writeable部分缓冲(链表)、内部_write函数、Transform实例必须实现的内部_transform函数以及系统提供的回调函数afterTransform。由于Transform实例同时拥有两部分缓冲,因此2个缓冲的存储、消耗的顺序也就需要了解,这对于后面使用原生Transform编写代码有很大的指导意义。
传统意义的流(即Readable和Writeable)的实现者都需要实现对应的内部函数_read()和_write(),对于Readable实例而言,_read函数用于准备从源文件中获取数据并添加到读缓冲中;对于Writeable实例_write函数则从写缓冲链表中一次刷入到磁盘中。它们分别对应了读写流程的首尾步骤,具体可以关注node中的Stream一文。
而Transform中的_read和_write函数的实现大有不同,由于需要兼顾流的处理,因此着重分析Transform的内部函数执行流程。
示例demo:
readable.pipe(transform);
以上段示例代码为例,transform作为消费者消费readable。
Transform的实例transform拥有transormState和readableState属性,保存了相关属性,如tranform状态信息、回调函数存储和编码等。transform作为消费者,会在其write函数中消费数据,在node中的Stream文中介绍了write函数的实现细节,通过内部调用_write函数实现数据的写入。而在Transform中_write函数已经重写:
1.保存transform收到的chunk数据、编码和函数(执行刷新写缓冲)
2.在一定条件下执行_read函数(当状态为非转换下,只要读缓冲大小未超过设定的大小,则执行_read)
如果一切顺利,readable的数据会顺利执行transform的**write->_write->_read**,那么原本负责填充读缓冲的_read在Transform中发生了哪些改变呢?
Transform.prototype._read = function(n) { var ts = this._transformState; if (ts.writechunk !== null && ts.writecb && !ts.transforming) { ts.transforming = true; this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); } else { // mark that we need a transform, so that any data that comes in // will get processed, now that we've asked for it. ts.needTransform = true; } };
可见,_read的实现非常简单,根据条件选择执行_transform函数。需要注意的是_read的参数n并未有使用,因为是否插入数据至读缓冲是由开发者在_transform中来决定。相信大家对_transform函数并不陌生,node规定Transform实例必须提供_transform函数,而该函数正是在_read中调用。
_transform有三个参数,第一个为待处理的chunk数据,第二个为编码,第三个为回调函数。前两个参数很好理解,我们可以在_transform中尽情的处理数据,最后调用回调函数完成处理。那么,这个回调函数究竟是什么? 它就是Transform架构图中的afterTransform函数,它有几个功能:
1.清空各种状态信息,如transformState对象的一些属性,用于下次处理数据使用
2.可选的保存处理结果至读缓冲区
3.刷新写缓冲区,执行下一阶段的数据流处理
可见,在afterTransform函数执行后,才基本宣告transform第一阶段的结束。为何是第一阶段呢?因为transform才完成了作为消费者(即Writeable)的作用,如果用户在_transform中传入了数据到写缓冲区,那么此时transform也同时是一个生产者,提供数据让后面的消费者消费数据,这就涉及到了Transform使用上的问题。
Transform的生产消费实例
const stream = require('stream') var c = 0; const readable = stream.Readable({ highWaterMark: 2, read: function () { var data = c < 26 "htmlcode">const transform = stream.Transform({ highWaterMark: 2, transform: function (buf, enc, next) { console.log('transform', buf.toString()) next(null, null) } })只需向next函数传入null即可,这样transform消费完数据后即宣告数据处理结束,读缓冲区始终为空。
第二种方案:
添加消费者:
const transform = stream.Transform({ highWaterMark: 2, transform: function (buf, enc, next) { console.log('transform', buf.toString()) next(null, buf) } }) readable.pipe(transform).pipe(process.stdout);transform实现不变,只是添加了消费者process.stdout。这样也同时保证了transform的读缓冲区处于可添加状态,也给了afterTransform函数刷新写缓冲区的机会,开启新的数据处理流程。
through2的实现
through2的重头戏在于Transform流,使用through2的API可方便的创建一个Transform实例,完成数据流的处理。
function through2 (construct) { return function (options, transform, flush) { if (typeof options == 'function') { flush = transform transform = options options = {} } if (typeof transform != 'function') transform = noop if (typeof flush != 'function') flush = null return construct(options, transform, flush) } } module.exports = through2(function (options, transform, flush) { var t2 = new DestroyableTransform(options) t2._transform = transform if (flush) t2._flush = flush return t2 })可见,through2模块仅仅是封装了Transform的构造函数,并封装了更为易用的objectMode模式。之所以建议使用through2创建Transform对象,不仅仅是因为其提供了方便的API,更主要的是为了兼容性。Transform对象是属于Stream2.0的特性,早先版本的node并没有实现,而通过through2创建的Transform实例在之前版本的node下仍可正常使用,这是由于through2并未引用node默认提供的stream模块,而是使用社区中较为流行的“readable-stream”模块。
总结
本文旨在深入through2中的使用的Transform流进行探究,并作为上一篇文章node中的stream的回顾和应用。通过文末简单的示例了解Transform在开发中可能出现的问题,学会随意切换Transform的生产者和消费者的身份,更好的指导实际开发。
以上所述是小编给大家介绍的node之Transform ,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]