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


Posted in Javascript onMarch 01, 2016

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()
 }
}

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

Javascript 相关文章推荐
javascript 伪数组实现方法
Oct 11 Javascript
基于jquery跨浏览器显示的file上传控件
Oct 24 Javascript
js调用webservice中的方法实现思路及代码
Feb 25 Javascript
Knockout数组(observable)使用详解示例
Nov 15 Javascript
jquery实现鼠标经过显示下划线的渐变下拉菜单效果代码
Aug 24 Javascript
JS实现的网页背景闪电闪烁效果代码
Oct 17 Javascript
JavaScript简单遍历DOM对象所有属性的实现方法
Oct 21 Javascript
js判断PC端与移动端跳转
Dec 24 Javascript
安装Node.js并启动本地服务的操作教程
May 12 Javascript
微信小程序实现类似微信点击语音播放效果
Mar 30 Javascript
JavaScript常用工具函数汇总(浏览器环境)
Sep 17 Javascript
解决vue项目打包上服务器显示404错误,本地没出错的问题
Nov 03 Javascript
JS实现图片平面旋转的方法
Mar 01 #Javascript
JS显示日历和天气的方法
Mar 01 #Javascript
jQuery使用模式窗口实现在主页面和子页面中互相传值的方法
Mar 01 #Javascript
jQuery获取某天的农历日期并判断是否除夕或新年的方法
Mar 01 #Javascript
jQuery实现获取table表格第一列值的方法
Mar 01 #Javascript
JavaScript Date对象详解
Mar 01 #Javascript
JavaScript通过使用onerror设置默认图像显示代替alt
Mar 01 #Javascript
You might like
MySql 按时间段查询数据方法(实例说明)
2008/11/02 PHP
php实现mysql封装类示例
2014/05/07 PHP
php微信公众平台开发类实例
2015/04/01 PHP
详谈phpAdmin修改密码后拒绝访问的问题
2017/04/03 PHP
PHP网站自动化配置的实现方法(必看)
2017/05/27 PHP
PHP递归实现文件夹的复制、删除、查看大小操作示例
2017/08/11 PHP
PHP 的Opcache加速的使用方法
2017/12/29 PHP
PHP使用pdo连接access数据库并循环显示数据操作示例
2018/06/05 PHP
向大师们学习Javascript(视频与PPT)
2009/12/27 Javascript
js读写(删除)Cookie实例详解
2013/04/17 Javascript
node.js中的buffer.length方法使用说明
2014/12/14 Javascript
jQuery对象的length属性用法实例
2014/12/27 Javascript
JavaScript使用push方法添加一个元素到数组末尾用法实例
2015/04/06 Javascript
剖析Node.js异步编程中的回调与代码设计模式
2016/02/16 Javascript
Bootstrap每天必学之标签页(Tab)插件
2020/08/09 Javascript
利用JS实现数字增长
2016/07/28 Javascript
微信小程序项目总结之点赞 删除列表 分享功能
2018/06/25 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
js 函数性能比较方法
2020/08/24 Javascript
[02:08]2018年度CS GO枪械皮肤设计大赛优秀作者-完美盛典
2018/12/16 DOTA
[01:30:15]DOTA2-DPC中国联赛 正赛 Ehome vs Aster BO3 第二场 2月2日
2021/03/11 DOTA
把大数据数字口语化(python与js)两种实现
2013/02/21 Python
Python中的默认参数实例分析
2018/01/29 Python
详解python之协程gevent模块
2018/06/14 Python
深入浅析python3中的unicode和bytes问题
2019/07/03 Python
Python3 批量扫描端口的例子
2019/07/25 Python
Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)
2021/02/22 Python
学点简单的Django之第一个Django程序的实现
2021/02/24 Python
英国领先的在线旅游和休闲零售商:lastminute.com
2019/01/23 全球购物
如何用Java实现列出某个目录下的所有子目录
2015/07/20 面试题
应届生程序员求职信
2013/11/05 职场文书
餐饮业创业计划书范文
2014/01/06 职场文书
知识竞赛活动方案
2014/02/18 职场文书
需求分析说明书
2014/05/09 职场文书
2015新年寄语(一句话)
2014/12/08 职场文书
签证扫盲贴,41个常见签证知识,需要的拿走
2019/08/09 职场文书