用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 相关文章推荐
firefox中JS读取XML文件
Dec 21 Javascript
不懂JavaScript应该怎样学
Apr 16 Javascript
Jquery下:nth-child(an+b)的使用注意
May 28 Javascript
JAVASCRIPT模式窗口中下载文件无法接收iframe的流
Oct 11 Javascript
JavaScript中的Primitive对象封装介绍
Dec 31 Javascript
jQuery EasyUI 菜单与按钮之创建简单的菜单和链接按钮
Nov 18 Javascript
基于JQuery的$.ajax方法进行异步请求导致页面闪烁的解决办法
May 10 Javascript
BootStrap中Table分页插件使用详解
Oct 09 Javascript
AngularJS模板加载用法详解
Nov 04 Javascript
微信小程序学习(4)-系统配置app.json详解
Jan 12 Javascript
判断iOS、Android以及PC端的示例代码
Nov 15 Javascript
如何写好一个vue组件,老夫的一年经验全在这了(推荐)
May 18 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
亚洲咖啡有什么?亚洲咖啡产地介绍 亚洲咖啡有什么特点?
2021/03/05 新手入门
深入PHP5中的魔术方法详解
2013/06/17 PHP
php三元运算符知识汇总
2015/07/02 PHP
php倒计时出现-0情况的解决方法
2016/07/28 PHP
php in_array() 检查数组中是否存在某个值详解
2016/11/23 PHP
javascript IE中的DOM ready应用技巧
2008/07/23 Javascript
extjs之去除s.gif的影响
2010/12/25 Javascript
利用js实现遮罩以及弹出可移动登录窗口
2013/07/08 Javascript
node.js中的fs.utimes方法使用说明
2014/12/15 Javascript
jQuery晃动层特效实现方法
2015/03/09 Javascript
jquery采用oop模式class类的使用示例
2016/01/22 Javascript
为什么JavaScript没有块级作用域
2016/05/22 Javascript
Bootstrap php制作动态分页标签
2016/12/23 Javascript
Javascript中从学习bind到实现bind的过程
2018/01/05 Javascript
基于jquery实现九宫格拼图小游戏
2018/11/30 jQuery
vue项目首屏加载时间优化实战
2019/04/23 Javascript
详解微信小程序文件下载--视频和图片
2019/04/24 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
解决Layui当中的导航条动态添加后渲染失败的问题
2019/09/25 Javascript
JQuery获得内容和属性方法解析
2020/05/30 jQuery
python操作xml文件示例
2014/04/07 Python
Python实现SMTP发送邮件详细教程
2021/03/02 Python
python自动点赞功能的实现思路
2020/02/26 Python
python基本算法之实现归并排序(Merge sort)
2020/09/01 Python
python 牛顿法实现逻辑回归(Logistic Regression)
2020/10/15 Python
AJAX检测用户名是否存在的方法
2021/03/24 Javascript
公司前台接待岗位职责
2013/12/03 职场文书
就业自荐书
2013/12/05 职场文书
优秀党员获奖感言
2014/02/18 职场文书
税务干部个人整改措施思想汇报
2014/10/10 职场文书
2015年乡镇财政工作总结
2015/05/19 职场文书
欧也妮葛朗台读书笔记
2015/06/30 职场文书
房产销售员2015年终工作总结
2015/10/22 职场文书
Python爬虫进阶之Beautiful Soup库详解
2021/04/29 Python
MySQL时区造成时差问题
2022/04/13 MySQL
webpack介绍使用配置教程详解webpack介绍和使用
2022/06/25 Javascript