资讯专栏INFORMATION COLUMN

使用 Node.js 中的流写工具时的两点小 tips

fizz / 3328人阅读

摘要:中的流十分强大,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递。本文将会提供两个在编写基于流的工具时,私以为有些用的两个。

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78777.html

相关文章

  • [转]nodejs Stream使用手册

    摘要:方法也可以接收一个参数表示数据请求着请求的数据大小,但是可读流可以根据需要忽略这个参数。读取数据大部分情况下我们只要简单的使用方法将可读流的数据重定向到另外形式的流,但是在某些情况下也许直接从可读流中读取数据更有用。 介绍本文介绍了使用 node.js streams 开发程序的基本方法。 We should have some ways of connecting programs ...

    luffyZh 评论0 收藏0
  • 《java 8 实战》读书笔记 -第四章 引入流

    摘要:第四章引入流一什么是流流是的新成员,它允许你以声明性方式处理数据集合通过查询语句来表达,而不是临时编写一个实现。 第四章 引入流 一、什么是流 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码。 下面两段代码都是用来返回低...

    jeyhan 评论0 收藏0
  • 正在暑假中的《课多周刊》(第1期)

    摘要:正在暑假中的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。原理微信热更新方案涨知识了,热更新是以后的标配。 正在暑假中的《课多周刊》(第1期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大的动力。 远上寒山石径...

    liukai90 评论0 收藏0
  • 正在暑假中的《课多周刊》(第1期)

    摘要:正在暑假中的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。原理微信热更新方案涨知识了,热更新是以后的标配。 正在暑假中的《课多周刊》(第1期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大的动力。 远上寒山石径...

    yintaolaowanzi 评论0 收藏0
  • Gulp思维——Gulp高级技巧

    摘要:我们创建一个可读流,并尝试使用和来进行转换,将最后得到的内容交给。它重新使用可读流中的文件名,然后在必要时创建文件夹使用。使用常规可读流时,你可以监听事件来检测数据碎片的到来不同的是,使用会将转换成的文件对象重新写入到流中。 本文翻译自Getting gulpy -- Advanced tips for using gulp.js 感受过gulp.js带来的兴奋过后,你需要的不仅仅是...

    ad6623 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<