专栏首页美团技术团队Node.js Stream - 基础篇

Node.js Stream - 基础篇

背景

在构建较复杂的系统时,通常将其拆解为功能独立的若干部分。这些部分的接口遵循一定的规范,通过某种方式相连,以共同完成较复杂的任务。譬如,shell通过管道|连接各部分,其输入输出的规范是文本流。

Node.js中,内置的Stream模块也实现了类似功能,各部分通过.pipe()连接。

鉴于目前国内系统性介绍Stream的文章较少,而越来越多的开源工具都使用了Stream,本系列文章将从以下几方面来介绍相关内容:

  1. 流的基本类型,以及Stream模块的基本使用方法。
  2. 流式处理与back pressure的工作原理。
  3. 如何开发流式程序,包括对GulpBrowserify的剖析,以及一个实战示例。

流的四种类型

Stream提供了以下四种类型的流:

使用Stream可实现数据的流式处理,如:

Readable

创建可读流。

实例:流式消耗迭代器中的数据。

实际使用时,new ToReadable(iterable)会返回一个可读流,下游可以流式的消耗迭代器中的数据。

执行上述代码,将会有100亿个随机数源源不断地写进标准输出流。

创建可读流时,需要继承Readable,并实现_read方法。

  • _read方法是从底层系统读取具体数据的逻辑,即生产数据的逻辑。
  • 在_read方法中,通过调用push(data)将数据放入可读流中供下游消耗。
  • 在_read方法中,可以同步调用push(data),也可以异步调用。
  • 当全部数据都生产出来后,必须调用push(null)来结束可读流。
  • 流一旦结束,便不能再调用push(data)添加数据。

可以通过监听data事件的方式消耗可读流。

  • 在首次监听其data事件后,readable便会持续不断地调用_read(),通过触发data事件将数据输出。
  • 第一次data事件会在下一个tick中触发,所以,可以安全地将数据输出前的逻辑放在事件监听后(同一个tick中)。
  • 当数据全部被消耗时,会触发end事件。

上面的例子中,process.stdout代表标准输出流,实际是一个可写流。下小节中介绍可写流的用法。

Writable

创建可写流。

前面通过继承的方式去创建一类可读流,这种方法也适用于创建一类可写流,只是需要实现的是_write(data, enc, next)方法,而不是_read()方法。

有些简单的情况下不需要创建一类流,而只是一个流对象,可以用如下方式去做:

  • 上游通过调用writable.write(data)将数据写入可写流中。write()方法会调用_write()将data写入底层。
  • 在_write中,当数据成功写入底层后,必须调用next(err)告诉流开始处理下一个数据。
  • next的调用既可以是同步的,也可以是异步的。
  • 上游必须调用writable.end(data)来结束可写流,data是可选的。此后,不能再调用write新增数据。
  • 在end方法调用后,当所有底层的写操作均完成时,会触发finish事件。

Duplex

创建可读可写流。

Duplex实际上就是继承了Readable和Writable的一类流。

所以,一个Duplex对象既可当成可读流来使用(需要实现_read方法),也可当成可写流来使用(需要实现_write方法)。

上面的代码中实现了_read方法,所以可以监听data事件来消耗Duplex产生的数据。

同时,又实现了_write方法,可作为下游去消耗数据。

因为它既可读又可写,所以称它有两端:可写端和可读端。

可写端的接口与Writable一致,作为下游来使用;可读端的接口与Readable一致,作为上游来使用。

Transform

在上面的例子中,可读流中的数据(0, 1)与可写流中的数据('a', 'b')是隔离开的,但在Transform中可写端写入的数据经变换后会自动添加到可读端。

Tranform继承自Duplex,并已经实现了_read和_write方法,同时要求用户实现一个_transform方法。

前面几节的例子中,经常看到调用data.toString()。这个toString()的调用是必需的吗?

本节介绍完如何控制流中的数据类型后,自然就有了答案。

在shell中,用管道(|)连接上下游。上游输出的是文本流(标准输出流),下游输入的也是文本流(标准输入流)。在本文介绍的流中,默认也是如此。

对于可读流来说,push(data)时,data只能是String或Buffer类型,而消耗时data事件输出的数据都是Buffer类型。对于可写流来说,write(data)时,data只能是String或Buffer类型,_write(data)调用时传进来的data都是Buffer类型。

也就是说,流中的数据默认情况下都是Buffer类型。产生的数据一放入流中,便转成Buffer被消耗;写入的数据在传给底层写逻辑时,也被转成Buffer类型。

但每个构造函数都接收一个配置对象,有一个objectMode的选项,一旦设置为true,就能出现“种瓜得瓜,种豆得豆”的效果。

Readable未设置objectMode时:

输出:

Readable设置objectMode后:

可见,设置objectMode后,push(data)的数据被原样地输出了。此时,可以生产任意类型的数据。

预告

Stream系列共三篇文章:

  • 第一部分:基础篇,介绍Stream接口的基本使用。
  • 第二部分:进阶篇,重点剖析Stream底层如何支持流式数据处理,及其back pressure机制。
  • 第三部分:实战篇。介绍如何使用Stream进行程序设计。从BrowserifyGulp总结出两种设计模式,并基于Stream构建一个为Git仓库自动生成changelog的应用作为示例。

参考文献

本文分享自微信公众号 - 美团点评技术团队(meituantech),作者:邹斌

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-07-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【沙龙干货】主题三:美团配送智能调度实践

    分享内容 ---- 外卖从2013、2014年开始到现在,最近两到三年发展速度非常快,在快速发展过程里边涌现了很多很有难度很新的问题,这个里边订单的调度是比较有...

    美团技术团队
  • 美团配送A/B评估体系建设与实践

    2019年5月6日,美团正式推出新品牌“美团配送”,发布了美团配送新愿景:“每天完成一亿次值得信赖的配送服务,成为不可或缺的生活基础设施。”现在,美团配送已经服...

    美团技术团队
  • WMRouter:美团外卖Android开源路由框架

    WMRouter是一款Android路由框架,基于组件化的设计思路,功能灵活,使用也比较简单。

    美团技术团队
  • Python爬虫教程:爬取微博数据【附源码】

    3、将获得的两项内容填入到weibo.py中,替换代码中的YOUR_USER_ID和#YOUR_COOKIE,运行代码。

    python学习教程
  • python 3D绘制立体几何

    py3study
  • Add Aplayer

    以下是相应的插入代码, 吸底模式可以在 body的任意地方 推荐顶部(效果可在本博客首页左下角查看) , 其他的就是在对应位置 可以在 md文件中插入使用 内联...

    BORBER
  • R语言ggplot2频率分布直方图小例子

    image.png 将其另存为csv格式。 以上数据来源 https://www.r-graph-gallery.com/220-basic-ggplot2-h...

    用户7010445
  • 数据地图多图层对象的颜色标度重叠问题解决方案

    一篇旧文,解决一个困扰已经的小技术问题,权当是学习ggplot2以来的整理回顾与查漏补缺。 ---- 今天这一篇是昨天推送的基础上进行了进一步的深化,主要讲如何...

    数据小磨坊
  • JQuery Deferred对象使用小结

    如下,打开页面时,获取默认选中的项目,同时也会初始化Combobox下拉框下拉列表数据

    授客
  • 云智AI预付费资源包 限时8折优惠!

    吼吼吼~ 云智AI 预付费资源包已经正式上线啦!!!从现起至1月31日限时8折优惠中~

    腾讯云AI中心

扫码关注云+社区

领取腾讯云代金券