用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 相关文章推荐
jquery遍历input取得input的name
Apr 27 Javascript
jquery实现的元素的left增加N像素 鼠标移开会慢慢的移动到原来的位置
Mar 21 Javascript
js如何取消事件冒泡
Sep 23 Javascript
JS拖动鼠标画出方框实现鼠标选区的方法
Aug 05 Javascript
JavaScript的面向对象编程基础
Aug 13 Javascript
原生JS改变透明度实现轮播效果
Mar 24 Javascript
原生JS发送异步数据请求
Jun 08 Javascript
vue树形结构获取键值的方法示例
Jun 21 Javascript
Layui数据表格跳转到指定页的实现方法
Sep 05 Javascript
vue $set 给数据赋值的实例
Nov 09 Javascript
java遇到微信小程序 "支付验证签名失败" 问题解决
Dec 22 Javascript
jquery实现图片放大镜效果
Dec 23 jQuery
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
php 表单数据的获取代码
2009/03/10 PHP
PHP版 汉字转码的实现详解
2013/06/09 PHP
解析php中两种缩放图片的函数,为图片添加水印
2013/06/14 PHP
php中spl_autoload详解
2014/10/17 PHP
javascript获得CheckBoxList选中的数量
2009/10/27 Javascript
javascript数字数组去重复项的实现代码
2010/12/30 Javascript
document.documentElement和document.body区别介绍
2013/09/16 Javascript
jQuery中校验时间格式的正则表达式小结
2013/09/22 Javascript
学习JavaScript设计模式之单例模式
2016/01/19 Javascript
JavaScript数组去重由慢到快由繁到简(优化篇)
2016/08/26 Javascript
如何快速上手Vuex
2017/02/14 Javascript
如何理解jQuery中的ajaxSubmit方法
2017/03/13 Javascript
vue router2.0二级路由的简单使用
2017/07/05 Javascript
详解JS构造函数中this和return
2017/09/16 Javascript
使用Vue-Router 2实现路由功能实例详解
2017/11/14 Javascript
vue+导航锚点联动-滚动监听和点击平滑滚动跳转实例
2019/11/13 Javascript
three.js欧拉角和四元数的使用方法
2020/07/26 Javascript
Python动态加载模块的3种方法
2014/11/22 Python
python数据清洗系列之字符串处理详解
2017/02/12 Python
对python同一个文件夹里面不同.py文件的交叉引用方法详解
2018/12/15 Python
pytorch 加载(.pth)格式的模型实例
2019/08/20 Python
Python continue语句实例用法
2020/02/06 Python
使用CSS3代码绘制可爱的Hello Kitty猫
2016/08/03 HTML / CSS
Becextech新西兰:数码单反相机和手机在线商店
2018/04/27 全球购物
物流专业大学生求职信范文
2013/10/28 职场文书
书法大赛策划方案
2014/06/04 职场文书
物理教育专业求职信
2014/06/25 职场文书
会计专业应届生自荐信
2014/06/28 职场文书
“四风”问题的主要表现和危害思想汇报
2014/09/19 职场文书
毕业实习自我鉴定范文2014
2014/09/26 职场文书
乡镇党的群众路线教育实践活动总结报告
2014/10/30 职场文书
2015年药房工作总结
2015/04/25 职场文书
保险公司岗前培训工作总结
2015/10/24 职场文书
Opencv实现二维直方图的计算及绘制
2021/07/21 Python
JS封装cavans多种滤镜组件
2022/02/15 Javascript
Python+Pillow+Pytesseract实现验证码识别
2022/05/11 Python