用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添加String.Format方法
Aug 11 Javascript
javascript window.opener的用法分析
Apr 07 Javascript
jQuery模拟超链接点击效果代码
Apr 21 Javascript
Javascript基础教程之关键字和保留字汇总
Jan 18 Javascript
JS实现的不规则TAB选项卡效果代码
Sep 18 Javascript
jQuery实现响应鼠标事件的图片透明效果【附demo源码下载】
Jun 16 Javascript
Node.js的环境安装配置(使用nvm方式)
Oct 11 Javascript
Vue使用vue-cli创建项目
Sep 01 Javascript
解读ES6中class关键字
Nov 20 Javascript
微信小程序input框中加入小图标的实现方法
Jun 19 Javascript
使用RN Animated做一个“添加购物车”动画的方法
Sep 12 Javascript
JS数组的常用10种方法详解
May 08 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下封装较好的数字分页方法
2010/11/23 PHP
PHP函数之日期时间函数date()使用详解
2013/09/09 PHP
PHP下的Oracle客户端扩展(OCI8)安装教程
2014/09/10 PHP
PHP中使用file_get_contents抓取网页中文乱码问题解决方法
2014/12/17 PHP
php 使用array函数实现分页
2015/02/13 PHP
PHP数组和explode函数示例总结
2015/05/08 PHP
php-redis中的sort排序函数总结
2015/07/08 PHP
thinkphp5.1 文件引入路径问题及注意事项
2018/06/13 PHP
PHP使用gearman进行异步的邮件或短信发送操作详解
2020/02/27 PHP
基于jquery的动态创建表格的插件
2011/04/05 Javascript
Javascript无阻塞加载具体方式
2013/06/28 Javascript
JS操作Cookie写入和读取实例代码
2013/10/20 Javascript
javascript函数式编程程序员的工具集
2015/10/11 Javascript
jQuery实现移动端滑块拖动选择数字效果
2015/12/24 Javascript
jquery插件ContextMenu设置右键菜单
2017/03/13 Javascript
Node.JS文件系统解析实例详解
2017/05/15 Javascript
js滚轮事件 js自定义滚动条的实现
2020/01/18 Javascript
JS猜数字游戏实例讲解
2020/06/30 Javascript
[11:01]2014DOTA2西雅图邀请赛 冷冷带你探秘威斯汀
2014/07/08 DOTA
Python 不同对象比较大小示例探讨
2014/08/21 Python
python实现飞机大战微信小游戏
2020/03/21 Python
Python高级特性切片(Slice)操作详解
2018/09/27 Python
使用Python快速制作可视化报表的方法
2019/02/03 Python
Python Subprocess模块原理及实例
2019/08/26 Python
详解如何用python实现一个简单下载器的服务端和客户端
2019/10/28 Python
浅谈django channels 路由误导
2020/05/28 Python
python中pandas库中DataFrame对行和列的操作使用方法示例
2020/06/14 Python
Paper Cape官网:美国婴儿和儿童服装品牌
2019/11/02 全球购物
工程力学硕士生的自我评价范文
2013/11/16 职场文书
房地产营销策划方案
2014/02/08 职场文书
文明礼貌主题班会
2015/08/14 职场文书
2016秋季田径运动会广播稿
2015/12/21 职场文书
电力培训学习心得体会
2016/01/11 职场文书
未来,这5大方向都很适合创业
2019/07/22 职场文书
详细介绍python类及类的用法
2021/05/31 Python