Node.js Stream - 基础篇 转载 node.js stream Innev 08月26日发布 [![](/upload/article/images/f20.jpg)](#) <h2 id="articleHeader0" style="font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-weight:500; line-height:1.1; color:rgb(64,64,64); margin-top:20px; margin-bottom:10px; font-size:1.75em; border-bottom:1px solid rgb(238,238,238); padding:0px 0px 5px"> 背景 </h2> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 在构建较复杂的系统时,通常将其拆解为功能独立的若干部分。这些部分的接口遵循一定的规范,通过某种方式相连,以共同完成较复杂的任务。譬如,shell通过管道<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">|</code>连接各部分,其输入输出的规范是文本流。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 在<a target="_blank" href="https://nodejs.org/" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">Node.js</a>中,内置的<a target="_blank" href="https://nodejs.org/api/stream.html" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">Stream</a>模块也实现了类似功能,各部分通过<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">.pipe()</code>连接。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 鉴于目前国内系统性介绍Stream的文章较少,而越来越多的开源工具都使用了Stream,本系列文章将从以下几方面来介绍相关内容: </p> <ol style="margin:5px 2px 5px 50px; padding:0px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px; line-height:28.8px"> <li style="line-height:28.8px"> 流的基本类型,以及<a target="_blank" href="https://nodejs.org/api/stream.html" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">Stream</a>模块的基本使用方法 </li> <li style="line-height:28.8px"> 流式处理与<a target="_blank" href="https://howtonode.org/streams-explained" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">back pressure</a>的工作原理 </li> <li style="line-height:28.8px"> 如何开发流式程序,包括对<a target="_blank" href="https://github.com/gulpjs/gulp" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">Gulp</a>与<a target="_blank" href="https://github.com/substack/node-browserify" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">Browserify</a>的剖析,以及一个实战示例。 </li> </ol> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 本文为系列文章的第一篇。 </p> <h2 id="articleHeader1" style="font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-weight:500; line-height:1.1; color:rgb(64,64,64); margin-top:20px; margin-bottom:10px; font-size:1.75em; border-bottom:1px solid rgb(238,238,238); padding:0px 0px 5px"> 流的四种类型 </h2> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> Stream提供了以下四种类型的流: </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> Stream = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'stream'</span>) <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> Readable = Stream.Readable <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> Writable = Stream.Writable <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> Duplex = Stream.Duplex <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> Transform = Stream.Transform </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 使用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Stream</code>可实现数据的流式处理,如: </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> fs = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'fs'</span>) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// `fs.createReadStream`创建一个`Readable`对象以读取`bigFile`的内容,并输出到标准输出</span> <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 如果使用`fs.readFile`则可能由于文件过大而失败</span> fs.createReadStream(bigFile).pipe(process.stdout) </code></pre> <h2 id="articleHeader2" style="font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-weight:500; line-height:1.1; color:rgb(64,64,64); margin-top:20px; margin-bottom:10px; font-size:1.75em; border-bottom:1px solid rgb(238,238,238); padding:0px 0px 5px"> Readable </h2> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 创建可读流。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 实例:流式消耗<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">迭代器</a>中的数据。 </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-meta" style="color:rgb(128,128,0); line-height:24.48px">'use strict'</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> Readable = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'stream'</span>).Readable <span class="hljs-class" style="line-height:24.48px"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">class</span> <span class="hljs-title" style="line-height:24.48px">ToReadable</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">extends</span> <span class="hljs-title" style="line-height:24.48px">Readable</span> </span>{ <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">constructor</span>(iterable) { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">super</span>() <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.iterator = <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">new</span> <span class="hljs-function" style="line-height:24.48px"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">function</span> *(<span class="hljs-params" style="line-height:24.48px"></span>) </span>{ <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">yield</span> * iterable } } <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 子类需要实现该方法</span> <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 这是生产数据的逻辑</span> _read() { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> res = <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.iterator.next() <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">if</span> (res.done) { <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 数据源已枯竭,调用`push(null)`通知流</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.push(<span class="hljs-literal" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">null</span>) } <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">else</span> { <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 通过`push`方法将数据添加到流中</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.push(res.value + <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'\n'</span>) } } } <span class="hljs-built_in" style="line-height:24.48px">module</span>.exports = ToReadable </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 实际使用时,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">new ToReadable(iterable)</code>会返回一个可读流,下游可以流式的消耗迭代器中的数据。 </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> iterable = <span class="hljs-function" style="line-height:24.48px"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">function</span> *(<span class="hljs-params" style="line-height:24.48px">limit</span>) </span>{ <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">while</span> (limit--) { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">yield</span> <span class="hljs-built_in" style="line-height:24.48px">Math</span>.random() } }(<span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">1e10</span>) <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> readable = <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">new</span> ToReadable(iterable) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 监听`data`事件,一次获取一个数据</span> readable.on(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'data'</span>, data => process.stdout.write(data)) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 所有数据均已读完</span> readable.on(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'end'</span>, () => process.stdout.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'DONE'</span>)) </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 执行上述代码,将会有100亿个随机数源源不断地写进标准输出流。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 创建可读流时,需要继承<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Readable</code>,并实现<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read</code>方法。 </p> <ul style="margin:0px 2px 0px 15px; padding:0px; font-size:16px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; line-height:28.8px"> <li style="line-height:28.8px"> <code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read</code>方法是从底层系统读取具体数据的逻辑,即生产数据的逻辑。 </li> <li style="line-height:28.8px"> 在<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read</code>方法中,通过调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">push(data)</code>将数据放入可读流中供下游消耗。 </li> <li style="line-height:28.8px"> 在<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read</code>方法中,可以同步调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">push(data)</code>,也可以异步调用。 </li> <li style="line-height:28.8px"> 当全部数据都生产出来后,<span style="font-weight:700">必须</span>调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">push(null)</code>来结束可读流。 </li> <li style="line-height:28.8px"> 流一旦结束,便不能再调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">push(data)</code>添加数据。 </li> </ul> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 可以通过监听<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>事件的方式消耗可读流。 </p> <ul style="margin:0px 2px 0px 15px; padding:0px; font-size:16px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; line-height:28.8px"> <li style="line-height:28.8px"> 在首次监听其<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>事件后,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">readable</code>便会持续不断地调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read()</code>,通过触发<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>事件将数据输出。 </li> <li style="line-height:28.8px"> 第一次<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>事件会在下一个tick中触发,所以,可以安全地将数据输出前的逻辑放在事件监听后(同一个tick中)。 </li> <li style="line-height:28.8px"> 当数据全部被消耗时,会触发<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">end</code>事件。 </li> </ul> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 上面的例子中,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">process.stdout</code>代表标准输出流,实际是一个可写流。下小节中介绍可写流的用法。 </p> <h2 id="articleHeader3" style="font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-weight:500; line-height:1.1; color:rgb(64,64,64); margin-top:20px; margin-bottom:10px; font-size:1.75em; border-bottom:1px solid rgb(238,238,238); padding:0px 0px 5px"> Writable </h2> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 创建可写流。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 前面通过继承的方式去创建一类可读流,这种方法也适用于创建一类可写流,只是需要实现的是<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_write(data, enc, next)</code>方法,而不是<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read()</code>方法。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 有些简单的情况下不需要创建一类流,而只是一个流对象,可以用如下方式去做: </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> Writable = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'stream'</span>).Writable <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> writable = Writable() <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 实现`_write`方法</span> <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 这是将数据写入底层的逻辑</span> writable._write = <span class="hljs-function" style="line-height:24.48px"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">function</span> (<span class="hljs-params" style="line-height:24.48px">data, enc, next</span>) </span>{ <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 将流中的数据写入底层</span> process.stdout.write(data.toString().toUpperCase()) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 写入完成时,调用`next()`方法通知流传入下一个数据</span> process.nextTick(next) } <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 所有数据均已写入底层</span> writable.on(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'finish'</span>, () => process.stdout.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'DONE'</span>)) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 将一个数据写入流中</span> writable.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'a'</span> + <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'\n'</span>) writable.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'b'</span> + <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'\n'</span>) writable.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'c'</span> + <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'\n'</span>) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 再无数据写入流时,需要调用`end`方法</span> writable.end() </code></pre> <ul style="margin:0px 2px 0px 15px; padding:0px; font-size:16px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; line-height:28.8px"> <li style="line-height:28.8px"> 上游通过调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">writable.write(data)</code>将数据写入可写流中。<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">write()</code>方法会调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_write()</code>将<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>写入底层。 </li> <li style="line-height:28.8px"> 在<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_write</code>中,当数据成功写入底层后,<span style="font-weight:700">必须</span>调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">next(err)</code>告诉流开始处理下一个数据。 </li> <li style="line-height:28.8px"> <code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">next</code>的调用既可以是同步的,也可以是异步的。 </li> <li style="line-height:28.8px"> 上游<span style="font-weight:700">必须</span>调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">writable.end(data)</code>来结束可写流,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>是可选的。此后,不能再调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">write</code>新增数据。 </li> <li style="line-height:28.8px"> 在<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">end</code>方法调用后,当所有底层的写操作均完成时,会触发<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">finish</code>事件。 </li> </ul> <h2 id="articleHeader4" style="font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-weight:500; line-height:1.1; color:rgb(64,64,64); margin-top:20px; margin-bottom:10px; font-size:1.75em; border-bottom:1px solid rgb(238,238,238); padding:0px 0px 5px"> Duplex </h2> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 创建可读可写流。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> <code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Duplex</code>实际上就是继承了<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Readable</code>和<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Writable</code>的一类流。<br style="line-height:10px" /> 所以,一个<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Duplex</code>对象既可当成可读流来使用(需要实现<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read</code>方法),也可当成可写流来使用(需要实现<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_write</code>方法)。 </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> Duplex = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'stream'</span>).Duplex <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> duplex = Duplex() <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 可读端底层读取逻辑</span> duplex._read = <span class="hljs-function" style="line-height:24.48px"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">function</span> (<span class="hljs-params" style="line-height:24.48px"></span>) </span>{ <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>._readNum = <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>._readNum || <span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">0</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">if</span> (<span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>._readNum > <span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">1</span>) { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.push(<span class="hljs-literal" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">null</span>) } <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">else</span> { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.push(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">''</span> + (<span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>._readNum++)) } } <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 可写端底层写逻辑</span> duplex._write = <span class="hljs-function" style="line-height:24.48px"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">function</span> (<span class="hljs-params" style="line-height:24.48px">buf, enc, next</span>) </span>{ <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// a, b</span> process.stdout.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'_write '</span> + buf.toString() + <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'\n'</span>) next() } <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 0, 1</span> duplex.on(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'data'</span>, data => <span class="hljs-built_in" style="line-height:24.48px">console</span>.log(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'ondata'</span>, data.toString())) duplex.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'a'</span>) duplex.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'b'</span>) duplex.end() </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 上面的代码中实现了<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read</code>方法,所以可以监听<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>事件来消耗<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Duplex</code>产生的数据。<br style="line-height:10px" /> 同时,又实现了<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_write</code>方法,可作为下游去消耗数据。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 因为它既可读又可写,所以称它有两端:可写端和可读端。<br style="line-height:10px" /> 可写端的接口与<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Writable</code>一致,作为下游来使用;可读端的接口与<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Readable</code>一致,作为上游来使用。 </p> <h2 id="articleHeader5" style="font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-weight:500; line-height:1.1; color:rgb(64,64,64); margin-top:20px; margin-bottom:10px; font-size:1.75em; border-bottom:1px solid rgb(238,238,238); padding:0px 0px 5px"> Transform </h2> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 在上面的例子中,可读流中的数据(0, 1)与可写流中的数据('a', 'b')是隔离开的,但在<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Transform</code>中可写端写入的数据经变换后会自动添加到可读端。<br style="line-height:10px" /> <code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Tranform</code>继承自<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Duplex</code>,并已经实现了<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_read</code>和<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_write</code>方法,同时要求用户实现一个<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_transform</code>方法。 </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-meta" style="color:rgb(128,128,0); line-height:24.48px">'use strict'</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> Transform = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'stream'</span>).Transform <span class="hljs-class" style="line-height:24.48px"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">class</span> <span class="hljs-title" style="line-height:24.48px">Rotate</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">extends</span> <span class="hljs-title" style="line-height:24.48px">Transform</span> </span>{ <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">constructor</span>(n) { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">super</span>() <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 将字母旋转`n`个位置</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.offset = (n || <span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">13</span>) % <span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">26</span> } <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 将可写端写入的数据变换后添加到可读端</span> _transform(buf, enc, next) { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> res = buf.toString().split(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">''</span>).map(c => { <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> code = c.charCodeAt(<span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">0</span>) <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">if</span> (c >= <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'a'</span> && c <= <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'z'</span>) { code += <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.offset <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">if</span> (code > <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'z'</span>.charCodeAt(<span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">0</span>)) { code -= <span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">26</span> } } <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">else</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">if</span> (c >= <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'A'</span> && c <= <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'Z'</span>) { code += <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.offset <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">if</span> (code > <span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'Z'</span>.charCodeAt(<span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">0</span>)) { code -= <span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">26</span> } } <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">return</span> <span class="hljs-built_in" style="line-height:24.48px">String</span>.fromCharCode(code) }).join(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">''</span>) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 调用push方法将变换后的数据添加到可读端</span> <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">this</span>.push(res) <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// 调用next方法准备处理下一个</span> next() } } <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">var</span> transform = <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">new</span> Rotate(<span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">3</span>) transform.on(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'data'</span>, data => process.stdout.write(data)) transform.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'hello, '</span>) transform.write(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'world!'</span>) transform.end() <span class="hljs-comment" style="color:rgb(128,128,128); font-style:italic; line-height:24.48px">// khoor, zruog!</span> </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> objectMode </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 前面几节的例子中,经常看到调用<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data.toString()</code>。这个<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">toString()</code>的调用是必需的吗?<br style="line-height:10px" /> 本节介绍完如何控制流中的数据类型后,自然就有了答案。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 在shell中,用管道(<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">|</code>)连接上下游。上游输出的是文本流(标准输出流),下游输入的也是文本流(标准输入流)。在本文介绍的流中,默认也是如此。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 对于可读流来说,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">push(data)</code>时,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>只能是<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">String</code>或<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Buffer</code>类型,而消耗时<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>事件输出的数据都是<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Buffer</code>类型。对于可写流来说,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">write(data)</code>时,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>只能是<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">String</code>或<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Buffer</code>类型,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">_write(data)</code>调用时传进来的<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">data</code>都是<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Buffer</code>类型。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 也就是说,流中的数据默认情况下都是<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Buffer</code>类型。产生的数据一放入流中,便转成<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Buffer</code>被消耗;写入的数据在传给底层写逻辑时,也被转成<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Buffer</code>类型。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 但每个构造函数都接收一个配置对象,有一个<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">objectMode</code>的选项,一旦设置为<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">true</code>,就能出现“种瓜得瓜,种豆得豆”的效果。 </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> <code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Readable</code>未设置<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">objectMode</code>时: </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> Readable = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'stream'</span>).Readable <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> readable = Readable() readable.push(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'a'</span>) readable.push(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'b'</span>) readable.push(<span class="hljs-literal" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">null</span>) readable.on(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'data'</span>, data => <span class="hljs-built_in" style="line-height:24.48px">console</span>.log(data)) </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 输出: </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><Buffer <span class="hljs-number" style="color:rgb(0,0,255); line-height:24.48px">61</span>> <span class="xml" style="line-height:24.48px"><span class="hljs-tag" style="line-height:24.48px; background:rgb(239,239,239)"><<span class="hljs-name" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">Buffer</span> <span class="hljs-attr" style="line-height:24.48px">62</span>></span> </span></code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> <code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">Readable</code>设置<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">objectMode</code>后: </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)"><span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> Readable = <span class="hljs-built_in" style="line-height:24.48px">require</span>(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'stream'</span>).Readable <span class="hljs-keyword" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">const</span> readable = Readable({ objectMode: <span class="hljs-literal" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">true</span> }) readable.push(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'a'</span>) readable.push(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'b'</span>) readable.push({}) readable.push(<span class="hljs-literal" style="font-weight:bold; color:rgb(0,0,128); line-height:24.48px">null</span>) readable.on(<span class="hljs-string" style="color:rgb(0,128,0); font-weight:bold; line-height:24.48px">'data'</span>, data => <span class="hljs-built_in" style="line-height:24.48px">console</span>.log(data)) </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 输出: </p> <pre style="overflow:auto; font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:13.6px; padding:6px; margin-top:0px; margin-bottom:10px; line-height:1.45; color:rgb(51,51,51); word-break:break-all; word-wrap:break-word; border:none; background-color:rgb(246,246,246)"><code class="language-javascript hljs" style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:undefined; padding:0.5em; color:rgb(0,0,0); margin:5px; white-space:pre-wrap; display:block; overflow-x:auto; background:rgb(255,255,255)">a b {} </code></pre> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 可见,设置<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">objectMode</code>后,<code style="font-family:Menlo,Monaco,Consolas,"Courier New",monospace; font-size:14.4px; padding:0px 4px; color:rgb(39,184,230); margin:5px; background-color:rgb(239,250,253)">push(data)</code>的数据被原样地输出了。此时,可以生产任意类型的数据。 </p> <h2 id="articleHeader6" style="font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-weight:500; line-height:1.1; color:rgb(64,64,64); margin-top:20px; margin-bottom:10px; font-size:1.75em; border-bottom:1px solid rgb(238,238,238); padding:0px 0px 5px"> 系列文章 </h2> <ul style="margin:0px 2px 0px 15px; padding:0px; font-size:16px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; line-height:28.8px"> <li style="line-height:28.8px"> 第一部分:<span style="color:#005fa9"><span style="">《Node.js Stream - 基础篇》</span></span>,介绍<span style="color:#005fa9"><span style="">Stream</span></span>接口的基本使用。 </li> <li style="line-height:28.8px"> 第二部分:<span style="color:#005fa9"><span style="">《Node.js Stream - 进阶篇》</span></span>,重点剖析Stream底层如何支持流式数据处理,及其<span style="color:#005fa9"><span style="">back pressure</span></span>机制。 </li> <li style="line-height:28.8px"> 第三部分:<span style="color:#005fa9"><span style="">《Node.js Stream - 实战篇》</span></span>,介绍如何使用Stream进行程序设计。从<span style="color:#005fa9"><span style="">Browserify</span></span>和<span style="color:#005fa9"><span style="">Gulp</span></span>总结出两种设计模式,并基于Stream构建一个为<span style="color:#005fa9"><span style="">Git</span></span>仓库自动生成changelog的应用作为示例。 </li> </ul> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> </p> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 参考文献 </p> <ul style="margin:0px 2px 0px 15px; padding:0px; font-size:16px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; line-height:28.8px"> <li style="line-height:28.8px"> GitHub,<a target="_blank" href="https://github.com/substack/browserify-handbook" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">substack/browserify-handbook</a> </li> <li style="line-height:28.8px"> GitHub,<a target="_blank" href="https://github.com/zoubin/streamify-your-node-program" style="color:rgb(0,95,169); text-decoration:none; background-color:transparent">zoubin/streamify-your-node-program</a> </li> </ul> <p style="margin-top:0px; margin-bottom:25px; line-height:28.8px; color:rgb(64,64,64); font-family:"Microsoft YaHei",Verdana,sans-serif,SimSun; font-size:16px"> 来自:http://tech.meituan.com/stream-basics.html </p> 08月26日发布 阅读原文 分享到: