用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 live函数
Dec 24 Javascript
ajax 同步请求和异步请求的差异分析
Jul 04 Javascript
javascript获得服务器端控件的ID的实现代码
Dec 28 Javascript
JS左右无缝滚动(一般方法+面向对象方法)
Aug 17 Javascript
含有CKEditor的表单如何提交
Jan 09 Javascript
js处理自己不能定义二维数组的方法详解
Mar 03 Javascript
用jquery写的菜单从左往右滑动出现
Apr 11 Javascript
全面解析JS字符串和正则表达式中的match、replace、exec等函数
Jul 01 Javascript
Bootstrap CSS组件之输入框组
Dec 17 Javascript
用jQuery实现可输入多选下拉组合框实例代码
Jan 18 Javascript
js的函数的按值传递参数(实例讲解)
Nov 16 Javascript
在vue中使用el-tab-pane v-show/v-if无效的解决
Aug 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
PHP之数组学习
2011/05/29 PHP
PHP5 的对象赋值机制介绍
2011/08/02 PHP
详谈配置phpstorm完美支持Codeigniter(CI)代码自动完成(代码提示)
2017/04/07 PHP
Discuz不使用插件实现简单的打赏功能
2019/03/21 PHP
javascript的对话框详解与参数
2007/03/08 Javascript
JS模块与命名空间的介绍
2013/03/22 Javascript
js判断FCKeditor内容是否为空的两种形式
2013/05/14 Javascript
限制textbox或textarea输入字符长度的JS代码
2013/10/16 Javascript
Javascript代码实现仿实例化类
2015/04/03 Javascript
原生js页面滚动延迟加载图片
2015/12/20 Javascript
微信小程序 框架详解及实例应用
2016/09/26 Javascript
使用jquery给指定的table动态添加一行、删除一行
2016/10/13 Javascript
Windows安装Node.js报错:2503、2502的解决方法
2017/10/25 Javascript
jQuery实现动态显示select下拉列表数据的方法
2018/02/05 jQuery
ajax请求+vue.js渲染+页面加载的示例
2018/02/11 Javascript
vue 组件中添加样式不生效的解决方法
2018/07/06 Javascript
详谈js的变量提升以及使用方法
2018/10/06 Javascript
js中int和string数据类型互相转化实例
2019/01/16 Javascript
一文了解Vue中的nextTick
2019/05/06 Javascript
JavaScript中的连续赋值问题实例分析
2019/07/12 Javascript
JS实现百度搜索框
2021/02/25 Javascript
python通过colorama模块在控制台输出彩色文字的方法
2015/03/19 Python
Python中文分词实现方法(安装pymmseg)
2016/06/14 Python
Python3 log10()函数简单用法
2019/02/19 Python
Python简易计算器制作方法代码详解
2019/10/31 Python
pytorch实现onehot编码转为普通label标签
2020/01/02 Python
Python 输出详细的异常信息(traceback)方式
2020/04/08 Python
在Windows上安装和配置 Jupyter Lab 作为桌面级应用程序教程
2020/04/22 Python
基于Python pyecharts实现多种图例代码解析
2020/08/10 Python
Html5页面二次分享的实现
2018/07/30 HTML / CSS
路政管理专业个人自荐信范文
2013/11/30 职场文书
军训鉴定表自我鉴定
2014/02/13 职场文书
教师节活动总结
2014/08/29 职场文书
建筑工地文明标语
2014/10/09 职场文书
单位介绍信格式
2015/01/31 职场文书
2015年学生管理工作总结
2015/05/26 职场文书