摘要:的源码仅仅就多行,本质上就是对于原生的流进行的封装,先来看下。是一个双工流,既可读,也可写,但是与还是有着一些区别,的写和读可以说是没有任何的关联,是两个缓冲区和管道互补干扰,而将其输入和输出是存在相互关联的,中间做了处理。
写在前面
through2经常被用于处理node的stream,假如使用过gulp的话,对于这个包一定不会陌生,如:
gulp.task("rewrite", () => { return gulp.src("./through/enter.txt") .pipe(through2.obj(function(chunk, enc, callback) { const { contents } = chunk; for (var i = 0; i < contents.length; i++) { if (contents[i] === 97) { contents[i] = 122; } } chunk.contents = contents; this.push(chunk); callback(); })) .pipe(gulp.dest("./dist")); });
这里将文件中所有的字符a转换为字符z,在写gulp插件时一定会应用到这个包,下面就来窥探一下这个使用率非常高的包。
Transform streamthrough2的源码仅仅就100多行,本质上就是对于node原生的transform流进行的封装,先来看下Transform stream。Transform是一个双工流,既可读,也可写,但是与Duplex还是有着一些区别,Duplex的写和读可以说是没有任何的关联,是两个缓冲区和管道互补干扰,而Transform将其输入和输出是存在相互关联的,中间做了处理。具体差别可以参考下面图片对比:
Duplex stream:
Transform stream:
Transform stream的两个缓存区相互关联,对于每个缓冲区来说,highWaterMark为阈值,超过阈值后,将会停止读或者写操作,如:
let i = 0; const readable = Readable({ highWaterMark: 2, read: function () { var data = i < 26 ? String.fromCharCode(i++ + 97) : null; console.log("push", data); this.push(data); } }); const transform = Transform({ highWaterMark: 2, transform: function (buf, enc, next) { console.log("transform", buf.toString()); next(null, buf); } }) readable.pipe(transform);
stream流向为:
由于阈值为2,所以只能push到f,这时readable的缓存区已满,transform的读缓存区和写缓存区已经满了(由于transform的两个缓存区的阈值为2,所以写缓存区在写入b之后就已经满了,后续不能继续写入),全部满之后,自然停止了读取,最终e,f存在A中,c,d存在B中,a,b存在C中,想要解决很简单,在添加一个流向就可以:
readable.pipe(transform).pipe(process.stdout);through2源码
在了解Transform stream之后,through2的源码非常的简单,就是对于其的一层封装,暴露出三个api(through2,through2.obj,through2.ctor)而且三者接收的参数一致,因为都是由一个工厂方法创造出的:
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) } }
来看一下through2对于Transform stream的再加工,也就是源码中的DestroyableTransform,与其名字一样,就是一个替我们实现好了destory方法的Transform stream:
DestroyableTransform.prototype.destroy = function(err) { if (this._destroyed) return this._destroyed = true var self = this // 触发destory后,close掉流 process.nextTick(function() { if (err) self.emit("error", err) self.emit("close") }) }
through2与through2.obj全部是创造出一个再加工后的Transform,区别如下:
后者开启了对象模式(objectMode属性为true),写入的参数不仅仅限制在string or uint8Array
后者降低了阈值(highWaterMark为16,而不是默认的16kb),这样做的原因,是为了和node的默认保持一致,具体可以参见这里
through2.ctor可以用来再次定制,其返回的是一个构造函数,用法可以参考下面:
const Tran = through.ctor(function(chunk, enc, callback) { console.log("transform", chunk.toString()); callback(null, chunk); }); const transform = new Tran();写在最后
stream在node中有着非常广泛的应用,但是它使用起来却不是那么友好,throgh2的出现可以减少使用上的麻烦,其原理也非常的简单;以上内容均为本人理解,如有错误还请指出,不胜感激~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/89349.html
摘要:地址采用流模式读取文件,每次读取一行数据对流的二次封装,更加易于使用普通用法等同于可以使号或者号参数每次流传完可以和一样做一些数据处理将文件中的全部替换为输出到完事写入完成对于需要相同处理过程的数据可以采用该方法创建一个构造函数完事 [github地址:https://github.com/ABCDdouyae...] csv2 采用流模式读取.csv文件,每次读取一行数据 throu...
摘要:地址采用流模式读取文件,每次读取一行数据对流的二次封装,更加易于使用普通用法等同于可以使号或者号参数每次流传完可以和一样做一些数据处理将文件中的全部替换为输出到完事写入完成对于需要相同处理过程的数据可以采用该方法创建一个构造函数完事 [github地址:https://github.com/ABCDdouyae...] csv2 采用流模式读取.csv文件,每次读取一行数据 throu...
摘要:地址采用流模式读取文件,每次读取一行数据对流的二次封装,更加易于使用普通用法等同于可以使号或者号参数每次流传完可以和一样做一些数据处理将文件中的全部替换为输出到完事写入完成对于需要相同处理过程的数据可以采用该方法创建一个构造函数完事 [github地址:https://github.com/ABCDdouyae...] csv2 采用流模式读取.csv文件,每次读取一行数据 throu...
摘要:优化性能这里有个有意思的地方。在多个参数的情况下。无则跳过,支持覆盖不可变。与原先不同的是,这里使用了一份初始化的对象引用来作为容器承载其余没有不同 through2 本质上是一种transform的流 被封装更好地操作流 var Transform = require(readable-stream/transform) , inherits = require(util).i...
阅读 2943·2023-04-25 19:20
阅读 786·2021-11-24 09:38
阅读 2040·2021-09-26 09:55
阅读 2430·2021-09-02 15:11
阅读 2015·2019-08-30 15:55
阅读 3610·2019-08-30 15:54
阅读 3148·2019-08-30 14:03
阅读 2962·2019-08-29 17:11