nodeJS

来源:互联网 发布:安卓大尺度直播软件 编辑:程序博客网 时间:2024/06/05 17:09

Stream 数据流

说明

Node.js 中的许多组件提供连续输入或可连续处理输入的功能,为了让这些组件行为一致,strean API 提供了一个抽象的接口,提供了常用的方法,以及数据流具体实现时需要使用的属性。数据流分为可读、可写、可读写,所有的流都是 Event Emitter 的实例,都可以主动触发事件。

参考

  • Node.js 文档
  • JavaScript 标准参考教程 Node.js

Node.js 中有以下两种缓存处理方式:

  1. 第一种是传统的读取文件方式,等到所有数据接收完毕,一次性从缓存中读取,然后处理,优点是简单直接自然,缺点是遇到大文件,需要换很长事件加载。才能进入数据处理步骤
  2. 第二种是 ‘数据流’ 的方式,每次只读取一小块数据,向了流水一样,每读取一小块数据,就触发一个事件,在这个事件中可以进行数据的处理,即在数据没有完全接受完成时,就开始处理它,可以提高程序的性能。

数据流是不同进程之间传递数据的一种方式。管道命令 pipe 就起到在不同命令之间连接数据流的作用。‘数据流’ 相当于把较大的数据拆分成为很小的部分,一个命令只要部署了数据流接口,就可以把一个流的输出接到另一个流的输入。数据流通过事件通信,具有 readable、writable、drain、data、end、close 等事件,既可以读取数据,也可以写入数据,每读取或者写入一段数据,就会触发一次 data 事件。

对象模式:使用 Node.js API 创建的流对象只能操作 strings 或者 buffer ,但是,通过一些第三方流的实现,你依然能够处理其他类型的 JavaScript 值(除了 null),这些流被认为是工作在‘对象模式’,在创建流的实例时,可以通过 objectMode 选项使流的实例切换到对象模式。

1. 可读数据流

‘可读数据流’ 用来产生数据。它表示数据的来源,只要一个对象提供 ‘可读数据流’, 就表示你可以从其中读取数据

    const Readable = require('stream').Readable;    const rb = new Readable();    rb.push('ttt'); // readable.push() 将数据添加到读取队列    rb.push('  kskskss');    rb.push(null); // 提示 readable 数据输入完成    /*        .pipe() 用于连接数据流        process.stdout 标准输出    */    rb.pipe(process.stdout);

1. 可读流存在两种模式和三种状态

两种模式

  1. flowing 流动模式,可读流自动从系统底层读取数据,并通过事件发射器接口尽快将数据提供给应用
  2. paused 暂停模式,必须显示的调用 stream.read() 可读流才会释放数据,新建的时候可读流处于暂停态

三种状态,可读流装台可以通过 readable._readableState.flowing 获得

  1. null 不存在数据消费者,可读流将不会产生数据
  2. true 数据流处在 流动模式,生成数据
  3. false 数据流处于 暂停模式,会暂停事件流,但是不会暂停数据生成,数据会堆积到流的内部缓存中

paused 暂停模式 转变为 flowing 流动模式的方法:

  1. 添加 data 事件的监听函数
  2. 调用 stream.resume() 方法
  3. 调用 stream.pipe() 方法将数据发送到下一个可写数据流

转化为流动模式时,如果没有 data 事件监听函数,也没有 pipe 方法的目的地,数据将会遗失


flowing 流动模式 转变为 paused 暂停模式 的方法:

  1. 如果不存在管道目标,通过调用 stream.pause() 实现
  2. 如果存在管道目标,1)取消 data 监听事件;2)调用 stream.unpipe() 方法移除管道目标
    const fs = require('fs');    const readableStream = fs.createReadStream('./test.txt');    let data = '';    let count = 0;    // 初始状态,未产生数据    console.log(readableStream._readableState.flowing); // null    // 开始监听 ‘data’事件, 可读流切换到 flowing 流动模式    readableStream.on('data', function (chunk) {        data +=  chunk;        count ++;        console.log(readableStream._readableState.flowing); // true 流动模式下状态为 true        if (count === 4) {            // 通过 pause 方法,暂停可读流,切换到 paused 暂停模式            readableStream.pause();             console.log('pause', readableStream._readableState.flowing); // pause false 暂停模式下状态为 false            setTimeout(function () {                // 通过 resume 方法 继续释放 data 事件,切换到 flowing 流动模式                readableStream.resume();                console.log('resume', readableStream._readableState.flowing); // resume true                        }, 1000)         }    })

2. 可读流中的事件

  1. close 在流或者底层资源关闭后触发,然后不会再有任何事件被触发,不是所有的 可读流都会触发此事件
  2. data 回调函数中有参数 chunk 表示数据片段,事件会在流将数据传递给消费者时触发,当流转换为 flowing 模式时触发
  3. end 在流中再没有数据可供消费时触发
  4. error 在底层系统内部出错从而不能产生数据或者流的实现视图传递错误数据时触发
  5. readable 在流中有数据可供读取时触发,当到达流的尾部时,也会触发此事件
    const fs = require('fs');    const readableStream = fs.createReadStream('./test.txt');    let data = '';    let count = 0;    readableStream.on('data', function (chunk) {        data += chunk;        count++;        if (count === 4) {            readableStream.push({name: 'test'}); // 一个错误的操作,可以触发 error 事件        }    })    /*        readable 事件监听系统中有可读的数据,表明了流有了新的动态,要么有新的数据、要么到达尾部        可以使用 readableStream.read() 方法返回可用的数据,如果到达尾部则返回 null    */     readableStream.on('readable', function () { // 不要与 'data' 事件一起用        while ((chunk = readableStream.read()) !== null) {            data2 += chunk;         }    })    readableStream.on('error', function (err) {        console.log('error', err);    })    readableStream.on('end', function () {        console.log('data', data);        console.log(count);    })    readableStream.on('close', function () {        console.log('close');    })

3. 可读流中的属性和方法

  1. readable 属性,表示当前数据流是否是一个可打开的数据流
  2. read([size]) 方法,从内部不缓存区抽出并返回一些数据,如果没有可读的数据返回 null , size 表示字节数
  3. setEncoding(encoding) 为从可读流读入的数据设置字符编码,可以使得数据流返回指定编码的字符串而不是 Buffer
  4. pause() 使得 flowing 模式的流停止触发 ‘data’ 事件,切换为 paused 模式
  5. resume() 使得 可读数据流 继续释放 data 事件,由 flowing 模式 切换为 paused 模式
  6. isPaused() 表示是否中断了可读数据流
  7. pipe(writableStream,[,options]) 从可读数据流中读取数据写入可写数据流,是一个自动传送数据的机制,必须在可读数据流上调用,参数为可写数据流,可以采用链式语法,默认可读流 end 时,可写流也会触发 stream.end() 结束写入,禁用默认行为需要设置第二个参数 {end: false}
  8. unpipe([destination]) 用于将之前通过 pipe() 方法绑定的流分离,参数是可选填的,表示要分离的流
    // 伪代码,有些接口不能一起使用    const fs = require('fs');    const readableStream = fs.createReadStream('./test.txt'); // 可读流    const writableStream = fs.createWriteStream('./output.txt'); // 可写流    let data = '';    let count = 0;    // setEncoding 设置字符编码    readableStream.setEncoding('utf-8');    // pipe 自动读取写入    readableStream.pipe(writableStream);    setTimeout(function () {        console.log('开始分离流');        // unpipe 分离流        readableStream.unpipe(writableStream);        console.log('结束分离流');    }, 10)    console.log('1', readableStream.readable); // true 当前为可打开数据流    readableStream.on('data', function (chunk) {        data += chunk;        count++;        console.log('2', readableStream.readable); // true 当前为可打开数据流        if (count === 4) {            // pause 方法,暂停可读流,切换到 paused 暂停模式            readableStream.pause();            // isPaused 判断是否为 paused 暂停模式            console.log(readableStream.isPaused()) // true            setTimeout(function () {                // resume 方法 继续释放 data 事件,切换到 flowing 流动模式                readableStream.resume();                console.log(readableStream.isPaused()); // false            }, 1000)         }    })    readableStream.on('readable', function () {        let chunk;        // read 从内部不缓存区抽出并返回一些数据        while (null !== (chunk = readableStream.read())) {            console.log(chunk);        }     })    readableStream.on('end', function () {        // console.log('data', data);        console.log(count);        console.log('3', readableStream.readable); // false 当前为不可打开数据流    })

2. 可写数据流

与可读数据流对应,可写数据流 用来接收数据,它允许将某个数据写入目的地,他是数据写入的一种抽象,素有 可写数据流 都实现了 stream.Writable 类定义的接口

1. 可写流的属性和方法

  1. writable 属性,如果流仍然打开,并且可写,就返回 true 否则返回 false
  2. write(chunk[, encoding][, callback]) 方法,用于向可写数据流写入数据,1)第一个参数表示写入的内容,可以是字符串可以是一个 stream 对象或 buffer 对象,对象模式下可以是任意 javascript 值,2)第二个参数可选,表示编码规范,3)第三个参数是缓冲数据输出时的回调函数;4)write() 方法具有返回值,如果要等待 drain 事件触发才能继续写入数据,将返回 false,否则返回 true
  3. cork() 方法,强制将所有写入数据都存放到内存中的缓冲区里,知道调用 stream.uncork()stream.end() 方法时,缓冲区里的数据才会被输出,可以用来避免向流中写入大量小数据块导致的性能下降问题
  4. uncork 输出在 cork() 方法调用后缓存在内存中的数据
  5. setDefaultEncoding(encoding) 方法,设置默认编码
  6. end([chunk][,encoding][,callback]) 方法, 结束写入数据,1)第一个参数,最后写入的数据;2)第二个参数,编码规范;3)流结束的回调函数。在调用了 stream.end() 方法之后,再调用 stream.write() 方法将会导致错误
  7. destroy() 摧毁这个流
    // 伪代码,有些接口不能一起使用    const fs = require('fs');    const readableStream = fs.createReadStream('./test.txt'); // 可读流    const writableStream = fs.createWriteStream('./output.txt'); // 可写流    let data = '';    let count = 0;    // 设置编码    writableStream.setDefaultEncoding('utf-8');    // cork 强制将所有写入数据都存放到内存中的缓冲区    writableStream.cork();    readableStream.on('data', function (chunk) {        // write 向可写数据流写入数据        const ret = writableStream.write(chunk, 'utf-8', function () {            console.log('finish');        });        count++;        // console.log(ret);        // writable 是否流仍然打开,并且可写        console.log(writableStream.writable); // true    })    readableStream.on('end', function () {        console.log(count);        console.log(writableStream.writable); // true        // destroy 摧毁这个流        writableStream.destroy();         // uncork 释放缓存在内存中的数据        writableStream.uncork();        // end 结束写入数据        writableStream.end('\n结束了!');        console.log(writableStream.writable); // false end() 之后不可写入     })

2. 可写流中的事件

  1. drain(耗尽)事件,writable.write(chunk) 返回 false 以后,当缓存数据全部写入完成,可以继续写入时,会触发drain事件,表示缓存空了。
  2. finishend()方法最后参数回调函数用法一致,都是 end 之后,缓存数据释放,触发 finish事件
  3. pipe 事件, 绑定在可写流上,在可读流触发 pipe 方法,将数据流导入此可写流上时触发事件,回调函数接受 可读流对象为参数
  4. unpipe 事件,可读流调用 unpipe() 方法将可写数据流移出写入的目的地时触发此事件,该事件的回调函数,接受发出该事件的“可读数据流”对象作为参数
  5. error 事件,如果写入数据或pipe数据时发生错误,就会触发该事件。
  6. close 事件,事件将在流或其底层资源(比如一个文件)关闭后触发
    // 伪代码,有些接口不能一起使用    const fs = require('fs');    const readableStream = fs.createReadStream('./test.txt'); // 可读流    const writableStream = fs.createWriteStream('./output.txt'); // 可写流    let data = '';    let count = 0;    // drain 缓存空了事件    writableStream.on('drain', function () {        console.log('drain');    })    // finish 结束    writableStream.on('finish', function () {        console.log('finish');    })    // pipe 管道流导入    writableStream.on('pipe', function (src) {        console.log('soming piping');    })    // unpipe 管道流中断    writableStream.on('unpipe', function (src) {        console.log('soming unpiping');    })    // error 报错    writableStream.on('error', function (err) {        console.log('error', err);    })    // close 最后关闭    writableStream.on('close', function () {        console.log('close');    })    // 通过管道导入流    readableStream.pipe(writableStream);    setTimeout(function () {        // 分离流,导入停止        readableStream.unpipe(writableStream);    }, 5)    readableStream.on('data', function (chunk) {        const ret = writableStream.write(chunk);        count++;        console.log('ret', ret);    })    readableStream.on('end', function () {        console.log(count);        writableStream.end('\n结束了!');    })

3. Duplex 流(可读、可写)

Duplex 流是同时实现了 Readable 和 Writable 接口的流。

4. Transform (变换)流

也是一种 Duplex 流,他的输入与输出时通过某种方式关联的

原创粉丝点击