搜索
首页web前端js教程聊聊Nodejs中的核心模块:stream流模块(看看如何使用)

本篇文章带大家详细理解一下Nodejs中的stream流模块,介绍一下stream流概念及用法,希望对大家有所帮助!

聊聊Nodejs中的核心模块:stream流模块(看看如何使用)

stream流模块,是Node中非常核心的一个模块,其它模块如fs、http等都基于流stream模块的实例。

而对于大多前端小白在刚入门Node的学习过程中,对于流的概念及使用还是不太好清晰的理解,因为在前端的工作中似乎很少有过关于"流"处理相关的应用。

1. 流,是什么?

单纯“流”这个字,我们很容易产生水流,流动等的概念。

官方定义:流,是用于在 Node.js 中处理流数据的抽象接口

从官方的定义中,我们可以看出:

  • 流,是Node提供的一种处理数据的工具
  • 流,是Node中的一种抽象接口

准确的理解,流,可以理解为数据流,它是一种用来传输数据的手段,在一个应用程序中,流,是一种有序的,有起点和终点的数据流。

造成我们对stream流不太好的理解的主要原因就是,它是一种抽象的概念。

2. 流,的具体使用场景

为了让我们能够清楚的理解stream模块,我们首先来以具体的应用场景来说明stream模块有哪些实际应用之处。

stream流,在Node中主要应用在大量数据处理的需求上,如fs对大文件的读取和写入、http请求响应、文件的压缩、数据的加密/解密等应用。

1.png

我们以上面的图片说明流的使用,水桶可以理解为数据源,水池可以理解为数据目标,中间连接的管道,我们可以理解为数据流,通过数据流管道,数据从数据源流向数据目标。

3. 流的分类

在Node中,流被分为4类:可读流,可写流,双工流,转换流。

  • Writable: 可以写入数据的流
  • Readable: 可以从中读取数据的流
  • DuplexReadable 和 Writable 的流
  • Transform: 可以在写入和读取数据时修改或转换数据的 Duplex 流

所有的流都是 EventEmitter 的实例。即我们可以通过事件机制监听数据流的变化。

4. 数据模式和缓存区

在深入学习4类流的具体使用之前,我们需要理解两个概念数据模式缓存区,有助于我们在接下来流的学习中更好的理解。

4.1 数据模式

Node.js API 创建的所有流都只对字符串和 Buffer(或 Uint8Array)对象进行操作。

4.2 缓存区

Writable和 Readable 流都将数据存储在内部缓冲区(buffer)中。

可缓冲的数据量取决于传给流的构造函数的 highWaterMark 选项, 对于普通的流,highWaterMark 选项指定字节的总数;对于在对象模式下操作的流,highWaterMark选项指定对象的总数。

highWaterMark 选项是阈值,而不是限制:它规定了流在停止请求更多数据之前缓冲的数据量。

当实现调用 stream.push(chunk) 时,数据缓存在 Readable 流中。 如果流的消费者没有调用 stream.read(),则数据会一直驻留在内部队列中,直到被消费。

一旦内部读取缓冲区的总大小达到 highWaterMark 指定的阈值,则流将暂时停止从底层资源读取数据,直到可以消费当前缓冲的数据

当重复调用 writable.write(chunk) 方法时,数据会缓存在 Writable 流中。

5. 可读流

5.1 流读取的流动与暂停

Readable 流以两种模式之一有效地运行:流动和暂停。

  • 流动模式:从系统底层读取数据并push()到缓存区,达到highWaterMark后 push() 会返回 false,资源停止流向缓存区,并触发data事件消费数据。

  • 暂停模式:所有的Readable流都是以Paused暂停模式开始,必须显式调用stream.read()方法来从流中读取数据。每一次数据达到缓存区都会触发一次 readable 事件,也就是每一次 push() 都会触发 readable。

  • 暂停模式切换到流动模式的方式:

    • 添加data事件句柄
    • 调用stream.resume()方法
    • 调用stream.pipe()方法将数据发送到 Writable
  • 流动模式切换到暂停模式的方式:

    • 如果没有管道目标,则通过调用 stream.pause() 方法。
    • 如果有管道目标,则删除所有管道目标。 可以通过调用 stream.unpipe()方法删除多个管道目标。

5.2 可读流常用示例

import path from 'path';
import fs, { read } from 'fs';

const filePath = path.join(path.resolve(), 'files', 'text.txt');

const readable = fs.createReadStream(filePath);
// 如果使用 readable.setEncoding() 方法为流指定了默认编码,则监听器回调将把数据块作为字符串传入;否则数据将作为 Buffer 传入。
readable.setEncoding('utf8');
let str = '';

readable.on('open', (fd) => {
  console.log('开始读取文件')
})
// 每当流将数据块的所有权移交给消费者时,则会触发 'data' 事件
readable.on('data', (data) => {
  str += data;
  console.log('读取到数据')
})
// 方法将导致处于流动模式的流停止触发 'data' 事件,切换到暂停模式。 任何可用的数据都将保留在内部缓冲区中。
readable.pause();
// 方法使被显式暂停的 Readable 流恢复触发 'data' 事件,将流切换到流动模式。
readable.resume();
// 当调用 stream.pause() 并且 readableFlowing 不是 false 时,则会触发 'pause' 事件。
readable.on('pause', () => {
  console.log('读取暂停')
})
// 当调用 stream.resume() 并且 readableFlowing 不是 true 时,则会触发 'resume' 事件。
readable.on('resume', () => {
  console.log('重新流动')
})
// 当流中没有更多数据可供消费时,则会触发 'end' 事件。
readable.on('end', () => {
  console.log('文件读取完毕');
})
// 当流及其任何底层资源(例如文件描述符)已关闭时,则会触发 'close' 事件。
readable.on('close', () => {
  console.log('关闭文件读取')
})
// 将 destWritable 流绑定到 readable,使其自动切换到流动模式并将其所有数据推送到绑定的 Writable。 数据流将被自动管理
readable.pipe(destWriteable)
// 如果底层流由于底层内部故障而无法生成数据,或者当流实现尝试推送无效数据块时,可能会发生这种情况。
readable.on('error', (err) => {
  console.log(err)
  console.log('文件读取发生错误')
})

6. 可写流

6.1 可写流的流动与暂停

writeable流 与 readable流 是比较相似的,数据流过来的时候,会直接写入到缓存区,当写入速度比较缓慢或者写入暂停时,数据流会在缓存区缓存起来;

当生产者写入速度过快,把队列池装满了之后,就会出现「背压」,这个时候是需要告诉生产者暂停生产的,当队列释放之后,writable流 会给生产者发送一个 drain 消息,让它恢复生产。

6.2 可写流示例

import path from 'path';
import fs, { read } from 'fs';

const filePath = path.join(path.resolve(), 'files', 'text.txt');
const copyFile = path.join(path.resolve(), 'files', 'copy.txt');

let str = '';
// 创建可读流
const readable = fs.createReadStream(filePath);
// 如果使用 readable.setEncoding() 方法为流指定了默认编码
readable.setEncoding('utf8');

// 创建可写流
const wirteable = fs.createWriteStream(copyFile);
// 编码
wirteable.setDefaultEncoding('utf8');

readable.on('open', (fd) => {
  console.log('开始读取文件')
})
// 每当流将数据块的所有权移交给消费者时,则会触发 'data' 事件
readable.on('data', (data) => {
  str += data;
  console.log('读取到数据');

  // 写入
  wirteable.write(data, 'utf8');
})

wirteable.on('open', () => {
  console.log('开始写入数据')
})
// 如果对 stream.write(chunk) 的调用返回 false,则 'drain' 事件将在适合继续将数据写入流时触发。
// 即生产数据的速度大于写入速度,缓存区装满之后,会暂停生产着从底层读取数据
// writeable缓存区释放之后,会发送一个drain事件让生产者继续读取
wirteable.on('drain', () => {
  console.log('继续写入')
})
// 在调用 stream.end() 方法之后,并且所有数据都已刷新到底层系统,则触发 'finish' 事件。
wirteable.on('finish', () => {
  console.log('数据写入完毕')
})

readable.on('end', () => {
  // 数据读取完毕通知可写流
  wirteable.end()
})
// 当在可读流上调用 stream.pipe() 方法将此可写流添加到其目标集时,则触发 'pipe' 事件。
// readable.pipe(destWriteable)
wirteable.on('pipe', () => {
  console.log('管道流创建')
})

wirteable.on('error', () => {
  console.log('数据写入发生错误')
})

更多node相关知识,请访问:nodejs 教程!!

以上是聊聊Nodejs中的核心模块:stream流模块(看看如何使用)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:掘金社区。如有侵权,请联系admin@php.cn删除
JavaScript在行动中:现实世界中的示例和项目JavaScript在行动中:现实世界中的示例和项目Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

了解JavaScript引擎:实施详细信息了解JavaScript引擎:实施详细信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:学习曲线和易用性Python vs. JavaScript:学习曲线和易用性Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

Python vs. JavaScript:社区,图书馆和资源Python vs. JavaScript:社区,图书馆和资源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

从C/C到JavaScript:所有工作方式从C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript引擎:比较实施JavaScript引擎:比较实施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

超越浏览器:现实世界中的JavaScript超越浏览器:现实世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)