宝哥软件园

用简单的方法理解Node.js流

编辑:宝哥软件园 来源:互联网 时间:2021-08-23

什么是蒸汽

流是Node.js中处理流数据的抽象接口

Streams不是Node.js的一个独特概念,它们是几十年前在Unix操作系统中引入的。

他们可以有效地处理文件读写、网络通信或任何类型的端到端信息交换。比如你写程序读文件,传统的方法是从头到尾把文件读入内存,然后处理。使用流,您可以逐块读取它并处理其内容,而无需将其全部保存在内存中。以下面的代码为例

const fs=require(' fs ');const RS=fs . CreateReadStream(' test . MD ');让数据=' ';rs.on('data ',function(chunk){ data=chunk;});rs.on('end ',function() {console.log(数据);});使用createReadStream创建一个数据读取流来读取test.md文件的内容,然后侦听数据事件,该事件在流将数据块传输给使用者后触发。在相应的事件处理程序中,拼接块。在结束事件中,打印到终端。在谈论流之前,您可以逐块读取文件内容,那么这是什么块,也就是区块?一般是Buffer,修改数据事件的eventHandler进行验证

rs.on('data ',function(chunk){ console . log(' chunk ',buffer . is buffer(chunk))//log true data=chunk;});流的工作模式可以具体表示为在内存中准备一个Buffer,然后在读取fs.read()时,将字节从磁盘逐步复制到Buffer。

为什么要使用Stream

使用Stream处理数据,主要是因为它有两个优点:

内存效率:不需要占用大量内存就可以处理数据;

时间效率:处理数据花费的时间更少,因为流逐块处理数据,而不是等待整个数据负载开始。

首先是内存效率,相比可以缓冲整个文件的fs.readFile,streaming充分利用了Buffer(大于8kb)不受V8内存控制的特点,利用堆外内存完成高效传输。请参考这篇博文,地址。与fs相比。FileSync,时间效率有一些优势,但与异步fs.readFile相比,优势不大。

流在节点中的使用

首先,用一张图片来了解Node.js中构建了哪些Stream接口

该图提供了一些Node.js本机流的例子,其中一些是可读和可写的。还有一些可读和可写的流,如TCP套接字、zlib和crypto。

特别注意:溪流的读写与环境息息相关。例如,HTTP响应在客户机上是可读的流,但在服务器上是可写的流。同时,需要注意的是,stdio流(stdin、stdout、stderr)在子进程上是相反的流。

用一个例子说明stream的用法

首先,使用以下脚本创建一个相对较大的文件(大约430 MB)

const fs=require(' fs ');const file=fs . createwritestream(' test . MD ');for(设I=0;i=1e6i ) {file.write('hello world。 n ');} file . end();在当前目录下,启动http服务

const http=require(' http ')const fs=require(' fs ')const server=http . createserver(function(req,RES){ fs . readfile(_ dirname '/test . MD ',(err,Data)={res. end (data)})}) server。听(3000),如图所示

const http=require(' http ')const fs=require(' fs ')const server=http . createserver((req,RES)={ const stream=fs . createreadstream(_ _ dirname '/test . MD ')stream . pipe(RES)})server . listen(3000)

时间减少了2秒多。这可以解释为:在读取文件内容而不改变内容的场景下,流可以只读取缓冲区,然后直接传输,无需额外转换,避免了丢失,提高了性能。在上述代码中,stream.pipe(.)被应用。它主要是对对流进行链式管道操作,如

一种数据流,如src.pipe(dest1)。管道(dest2)是自动管理的。

如果可读流出现错误,目标可写流不会自动关闭,所有流都需要手动关闭,以避免内存泄漏。

通常在使用pipe方法时,不需要使用事件,但是如果场景需要以更加灵活和定制的方式使用流,则应该考虑事件。

流事件

在上面的例子中,我们使用可读流的数据和结束事件来控制文件的读取,例如,这本质上与管道方法相同

#可读. pipe(可写)可读. on('data ',(chunk)={可写. write(chunk);});可读. on('end ',()={可写. end();});但是,使用事件将更加灵活和可控。

图中简要列出了可读流和可写流的相关事件和方法,其中最重要的是

可读流:

数据事件:每当流经过大块数据时触发;End事件:当流中没有要发送的数据时触发。可写流:

排空事件:当数据可以继续写入流时,将触发该事件;完成事件:在处理完所有数据块后触发。不同类型的流

除了上面提到的读和写流,还有两种类型:双工和转换:

可读:您可以接收数据,但不能向其发送数据。当您将数据推入可读流时,它将被缓冲,直到消费者开始读取数据;可写:可以发送数据,但不能从中接收数据;双面:可读写;Tranform:和Duplex一样,它是可写可读的,但是它的输出和它的输入有关。如何创建可读的流

这里只简单介绍一下。详情见流模块。

常量流=需要('流')常量可读流=新流。可读()可读流。_ Read=(大小)={console。log ('read ',size)}使用流模块初始化一个可读的流,然后向它发送数据

readableStream.push('hi!')readableStream.push('ho!'如何创建可写流

为了创建可写流,有必要扩展基本的可写对象并实现其_write方法。

Const stream=require(' stream ')Const可写stream=newstream .可写()实现了_write方法:

可写流。_ write=(区块,编码,下一个)={console。日志(块。tostring ()) next ()}是用上面的例子实现的

使用readableStream读入数据并将其输出到writableStream

const Stream=require(' Stream ')const readableStream=new Stream。可读()readableStream。_read=(大小)={console.log('read ',size)} const writableStream=new Stream。可写()可写流。_write=(chunk,encoding,next)={console.log('write ',chunk . tostring())next()} readablestream . pipe(writableStream)readablestream . push(' hi!')readableStream.push('ho!')/* log:read 16384write hi!写嗬!*/以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。

更多资讯
游戏推荐
更多+