【译】你所要知道关于 Node.js Streams 的一切

论坛 期权论坛     
选择匿名的用户   2021-5-30 02:23   122   0
<blockquote>
原文地址:
<a href="https://link.zhihu.com/?target&#61;https%3A//medium.freecodecamp.org/node-js-streams-everything-you-need-to-know-c9141306be93">https://medium.freecodecamp.org/node-js-streams-everything-you-need-to-know-c9141306be93</a>
</blockquote>
<p>Node.js streams(流)因其晦涩难懂以及难以使用而闻名。不过读了这篇文章之后,这些都难不倒你了。</p>
<p> </p>
<p>这几年,很多工程师都开发了一些为了使 stream 更易用的包。而这篇文章将聚焦于官方的 <a href="https://link.zhihu.com/?target&#61;https%3A//nodejs.org/api/stream.html">Node.js streams 文档</a>。</p>
<p> </p>
<blockquote>
<em>Stream 是 Node.js 中最好的但又最被大家所误解东西。</em>
<br>
<br> —— Dominic Tarr
</blockquote>
<p> </p>
<h2><strong>流(Stream)到底是什么?</strong></h2>
<p> </p>
<p>流就是一系列的数据——就跟数组或者字符串一样。有一点不同,就是 stream 可能无法在一次性全部可用,且它们不需要与内存完全合槽。这么一来,stream 在处理大量数据,或者操作一个一次只给出一部分数据的数据源的时候显得格外有用。</p>
<p> </p>
<p>其实,流不只是在操作大量数据的时候有用。它还为在代码中使用各种强大的组合类功能提供能力。例如,我们在 Linux 命令行中可以通过管道(pipe)来完成一些组合性的命令,在 Node.js 的流中也能实现。</p>
<p> </p>
<pre class="blockcode"><code>~/learn-node $ grep -R exports * | wc -l 6</code></pre>
<p> </p>
<p>上面的命令行在 Node.js 中可以这么实现:</p>
<p> </p>
<pre class="blockcode"><code>const grep &#61; ... // grep 输出流
const wc &#61; ... // wc 输入流

grep.pipe(wc)
</code></pre>
<p> </p>
<p>很多 Node.js 的内置模块都是基于流接口的:</p>
<p> </p>
<p><img alt="" class="blockcode" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-80ba7c87fc007955b47ad31ec012e5ee" width="1200"></p>
<p> </p>
<p>上图列表中就是一些使用了流的原生 Node.js 对象。其中有一些对象甚至是既可读又可写的,例如 TCP socket、zlib 以及 crypto 等。</p>
<p> </p>
<p>值得注意的是上面说的一些对象也是彼此紧密联系的。例如 HTTP 响应在客户端中是一个可读流,而在服务端则是一个可写流。毕竟在 HTTP 场景中,我们在客户端侧是从相应对象(<code>http.IncommingMessage</code>)读取数据,而在服务端则是写入数据(<code>http.ServerResponse</code>)。</p>
<p> </p>
<p>还需要注意的是,<code>stdio</code> 相应的流(<code>stdin</code>, <code>stdout</code>, <code>stderr</code>)在子进程中与主进程都是相反的流类型。这样一来主进程和子进程直接就可以方便地 pipe <code>stdio</code> 数据了。</p>
<p> </p>
<h2><strong>小试牛刀</strong></h2>
<p> </p>
<p>Talk is cheap, show me the code. 让我们来看看一个例子,来演示流中的内存差异。</p>
<p> </p>
<p>首先创建一个大大大大大大大文件:</p>
<p> </p>
<pre class="blockcode"><code>const fs &#61; require(&#39;fs&#39;);
const file &#61; fs.createWriteStream(&#39;./big.file&#39;);

for(let i&#61;0; i&lt;&#61; 1e6; i&#43;&#43;) {
  file.write(&#39;Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n&#39;);
}

file.end();
</code></pre>
<p> </p>
<p>看看我在创建文件的时候用了什么。一个可写流(Writable stream)!</p>
<p> </p>
<p><code>fs</code> 模块可以让你用流来写入或者读取文件。在上面的例子中,我们在一个一百万次的循环中用一个可写流写了一个大文件 <code>big.file</code>。</p>
<p> </p>
<p>运行完这段代码后,你会得到一个将近 400 MB 的文件。</p>
<p> </p>
<p>接下去是一个托管这个 <code>big.file</code> 的 Node.js web 服务端:</p>
<p> </p>
<pre class="blockcode"><code>const fs &#61; require(&#39;fs&#39;);
const server &#61; require(&#39;http&#39;).createServer();

server.on(&#39;request&#39;, (req, res) &#61;&gt; {
  fs.readFile(&#39;./big.file&#39;, (err, data) &#61;&gt; {
    if (err) throw err;
  
    res.end(data);
  });
});

server.listen(8000);
</code></pre>
<p> </p>
<p>当服务端进来一个请求,它就会通过 <code>fs.readFile</code> 来异步读取文件并返回。但是,哇,你看它并没有阻塞住我们的事件循环,或者其它一些东西。真腻害,是还是是还是是呢?</p>
<p> </p>
<p>到底是不是呢?我们先来看看服务端到底发生了什么吧,启动服务端,然后看看内存消耗。</p>
<p> </p>
<
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP