用NODE.JS中的流编写工具是要注意的事项

Node.js中的流十分强大,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递。正因为它如此好用,所以在实战中我们常常基于它来编写一些工具 函数/库 ,但往往又由于自己对流的某些特性的疏忽,导致写出的 函数/库 在一些情况会达不到想要的效果,或者埋下一些隐藏的地雷。本文将会提供两个在编写基于流的工具时,私以为有些用的两个tips。

一,警惕EVENTEMITTER内存泄露

在一个可能被多次调用的函数中,如果需要给流添加事件监听器来执行某些操作。那么则需要警惕添加监听器而导致的内存泄露:

'use strict';
const fs = require('fs');
const co = require('co');

function getSomeDataFromStream (stream) {
 let data = stream.read();
 if (data) return Promise.resolve(data);

 if (!stream.readable) return Promise.resolve(null);

 return new Promise((resolve, reject) => {
  stream.once('readable', () => resolve(stream.read()));
  stream.on('error', reject);
  stream.on('end', resolve);
 })
}

let stream = fs.createReadStream('/Path/to/a/big/file');

co(function *() {
 let chunk;
 while ((chunk = yield getSomeDataFromStream(stream)) !== null) {
  console.log(chunk);
 }
}).catch(console.error);

在上述代码中,getSomeDataFromStream函数会在通过监听error事件和end事件,来在流报错或没有数据时,完成这个Promise。然而在执行代码时,我们很快就会在控制台中看到报警信息:(node) warning: possible EventEmitter memory leak detected. 11 error listeners added. Use emitter.setMaxListeners() to increase limit.,因为我们在每次调用该函数时,都为传入的流添加了一个额外的error事件监听器和end事件监听器。为了避免这种潜在的内存泄露,我们要确保每次函数执行完毕后,清除所有此次调用添加的额外监听器,保持函数无污染:

function getSomeDataFromStream (stream) {
 let data = stream.read();
 if (data) return Promise.resolve(data);

 if (!stream.readable) return Promise.resolve(null);

 return new Promise((resolve, reject) => {
  stream.once('readable', onData);
  stream.on('error', onError);
  stream.on('end', done);

  function onData () {
   done();
   resolve(stream.read());
  }

  function onError (err) {
   done();
   reject(err);
  }

  function done () {
   stream.removeListener('readable', onData);
   stream.removeListener('error', onError);
   stream.removeListener('end', done);
  }
 })
}

二,保证工具函数的回调在处理完毕数据后才被调用

工具函数往往会对外提供一个回调函数参数,待处理完流中的所有数据后,带着指定值触发,通常的做法是将回调函数的调用挂在流的end事件中,但如果处理函数是耗时的异步操作,回调函数则可能在所有数据处理完毕前被调用:

'use strict';
const fs = require('fs');

let stream = fs.createReadStream('/Path/to/a/big/file');

function processSomeData (stream, callback) {
 stream.on('data', (data) => {
  // 对数据进行一些异步耗时操作
  setTimeout(() => console.log(data), 2000);
 });

 stream.on('end', () => {
  // ...
  callback()
 })
}

processSomeData(stream, () => console.log('end'));

以上的代码callback回调可能会在数据并未被全部处理时就被调用,因为流的end事件的触发时机仅仅是在流中的数据被读完时。所以我们需要额外地对数据是否已处理完进行检查:

function processSomeData (stream, callback) {
 let count = 0;
 let finished = 0;
 let isEnd = false;

 stream.on('data', (data) => {
  count++;
  // 对数据进行一些异步耗时操作
  setTimeout(() => {
   console.log(data);
   finished++;
   check();
  }, 2000);
 });

 stream.on('end', () => {
  isEnd = true;
  // ...
  check();
 })

 function check () {
  if (count === finished && isEnd) callback()
 }
}

这样一来,回调便会在所有数据都处理完毕后触发了。

(0)

相关推荐

  • Nodejs Stream 数据流使用手册

    1.介绍 本文介绍了使用 node.js streams 开发程序的基本方法. <code class="hljs mizar">"We should have some ways of connecting programs like garden hose--screw in another segment when it becomes necessary to massage data in another way. This is the way of

  • 详解Node.js串行化流程控制

    串行任务:需要一个接着一个坐的任务叫做串行任务. 可以使用回调的方式让几个异步任务按顺序执行,但如果任务过多,必须组织一下,否则过多的回调嵌套会把代码搞得很乱. 为了用串行化流程控制让几个异步任务按顺序执行,需要先把这些任务按预期的执行顺序放到一个数组中,这个数组将起到队列的作用:完成一个任务后按顺序从数组中取出下一个. 数组中的每个任务都是一个函数.任务完成后应该调用一个处理器函数,告诉它错误状态和结果. 为了演示如何实现串行化流程控制,我们准备做个小程序,让它从一个随机选择的RSS预定源中获

  • Node.js中流(stream)的使用方法示例

    前言 本文主要给大家介绍了关于Node.js 流(stream)的使用方法,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 流是基于事件的API,用于管理和处理数据,而且有不错的效率.借助事件和非阻塞I/O库,流模块允许在其可用的时候动态处理,在其不需要的时候释放掉. 使用流的好处 举一个读取文件的例子: 使用fs.readFileSync同步读取一个文件,程序会被阻塞,所有的数据都会被读取到内存中. 换用fs.readFile读取文件,程序不会被阻塞,但是所有的数据依旧会被一次性

  • 深入nodejs中流(stream)的理解

    nodejs的fs模块并没有提供一个copy的方法,但我们可以很容易的实现一个,比如: var source = fs.readFileSync('/path/to/source', {encoding: 'utf8'}); fs.writeFileSync('/path/to/dest', source); 这种方式是把文件内容全部读入内存,然后再写入文件,对于小型的文本文件,这没有多大问题,比如grunt-file-copy就是这样实现的.但是对于体积较大的二进制文件,比如音频.视频文件,动

  • Node.js中的缓冲与流模块详细介绍

    缓冲(buffer)模块 js起初就是为浏览器而设计的,所以能很好的处理unicode编码的字符串,但不能很好的处理二进制数据.这是Node.js的一个问题,因为Node.js旨在网络上发送和接收经常是以二进制格式传输的数据.比如: - 通过TCP连接发送和接收数据:  - 从图像或者压缩文件读取二进制数据:  - 从文件系统读写数据:  - 处理来自网络的二进制数据流 而Buffer模块为Node.js带来了一种存储原始数据的方法,于是可以再js的上下文中使用二进制数据.每当需要在Node.j

  • 用NODE.JS中的流编写工具是要注意的事项

    Node.js中的流十分强大,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递.正因为它如此好用,所以在实战中我们常常基于它来编写一些工具 函数/库 ,但往往又由于自己对流的某些特性的疏忽,导致写出的 函数/库 在一些情况会达不到想要的效果,或者埋下一些隐藏的地雷.本文将会提供两个在编写基于流的工具时,私以为有些用的两个tips. 一,警惕EVENTEMITTER内存泄露 在一个可能被多次调用的函数中,如果需要给流添加事件监听器来执行某些操作.那么则需要警惕添加监听器而导致的内

  • node.js中stream流中可读流和可写流的实现与使用方法实例分析

    本文实例讲述了node.js中stream流中可读流和可写流的实现与使用方法.分享给大家供大家参考,具体如下: node.js中的流 stream 是处理流式数据的抽象接口.node.js 提供了很多流对象,像http中的request和response,和 process.stdout 都是流的实例. 流可以是 可读的,可写的,或是可读可写的.所有流都是 events 的实例. 一.流的类型 node.js中有四种基本流类型: 1.Writable 可写流 (例:fs.createWriteS

  • node.js中npm包管理工具用法分析

    本文实例讲述了node.js中npm包管理工具用法.分享给大家供大家参考,具体如下: 现在安装node.js,默认就会帮我们装上了npm包管理工具,npm主要用来下载,安装,管理第三方模块. 创建一个包描述文件: npm init [-y] 查看包的信息 npm info <package-name> 查看包的版本信息 npm info <package-name> versions 安装指定的包: npm install <package-name> 默认会安装在当前

  • Node.js中的流(Stream)介绍

    什么是流? 说到流,就涉及到一个*nix的概念:管道--在*nix中,流在Shell中被实现为可以通过 |(管道符) 进行桥接的数据,一个进程的输出(stdout)可被直接作为下一个进程的输入(stdin). 在Node中,流(Stream)的概念与之类似,代表一种数据流可供桥接的能力. pipe 流化的精髓在于 .pipe()方法.可供桥接的能力,在于数据流的两端(上游/下游 或称为 读/写流)以一个 .pipe()方法进行桥接. 伪代码的表现形式为: 复制代码 代码如下: //上游.pipe

  • Node.js中你不可不精的Stream(流)

    一.什么是Stream(流) 流(stream)在 Node.js 中是处理流数据的抽象接口(abstract interface). stream 模块提供了基础的API.使用这些API可以很容易地来构建实现流接口的对象.例如, HTTP 请求 和 process.stdout 就都是流的实例. 流可以是可读的.可写的,或是可读写的.注意,所有的流都是 EventEmitter 的实例. 二.流的类型 Node.js 中有四种基本的流类型: Readable - 可读的流 (例如 fs.cre

  • 在Node.js中实现文件复制的方法和实例

    Node.js 本身并没有提供直接复制文件的 API,如果想用 Node.js 复制文件或目录,需要借助其他的 API 来实现.复制单个的文件可以直接用 readFile.writeFile,这样比较简便.如果是复制一个目录下的所有文件,目录下可能还包含了子目录,那么此时就需要用到更高级点的 API 了. 流 流是 Node.js 移动数据的方式,Node.js 中的流是可读/可写的,HTTP 和文件系统模块都有用到流.在文件系统中,使用流来读取文件的时候,对于一个大文件可能并不会一次性读取完,

  • 浅析Node.js 中 Stream API 的使用

    本文由浅入深给大家介绍node.js stream api,具体详情请看下文吧. 基本介绍 在 Node.js 中,读取文件的方式有两种,一种是用 fs.readFile ,另外一种是利用 fs.createReadStream 来读取. fs.readFile 对于每个 Node.js 使用者来说最熟悉不过了,简单易懂,很好上手.但它的缺点是会先将数据全部读入内存,一旦遇到大文件的时候,这种方式读取的效率就非常低下了. 而 fs.createReadStream 则是通过 Stream 来读取

  • Node.js插件的正确编写方式

    Node.js在利用JavaScript编写后端方面效果拔群,值得我们多加尝试.不过如果大家需要一些无法直接使用的功能甚至是根本无从实现的模块使用,那么能否从C/C++库当中引入此类成果呢?答案是肯定的,大家要做的就是编写一款插件,并借此在自己的JavaScript代码中使用其它代码库的资源.下面我们就一同开始今天的探询之旅. 介绍 正如Node.js在官方说明文档中所言,插件是以动态方式进行链接的共享式对象,能够将JavaScript代码与C/C++库接驳起来.这意味着我们可以引用任何来自C/

  • 基于Node.js的JavaScript项目构建工具gulp的使用教程

    npm install gulp --save-dev 什么是gulp? gulp是新一代的前端项目构建工具,你可以使用gulp及其插件对你的项目代码(less,sass)进行编译,还可以压缩你的js和css代码,甚至压缩你的图片,gulp仅有少量的API,所以非常容易学习. gulp 使用 stream 方式处理内容.Node催生了一批自动化工具,像Bower,Yeoman,Grunt等. gulp和grunt的异同点 易于使用:采用代码优于配置策略,Gulp让简单的事情继续简单,复杂的任务变

随机推荐