aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/streams_api
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
commit33058f2b292b3a581333bdfb21b8f671898c5060 (patch)
tree51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/api/streams_api
parent8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff)
downloadtranslated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip
initial commit
Diffstat (limited to 'files/zh-cn/web/api/streams_api')
-rw-r--r--files/zh-cn/web/api/streams_api/index.html145
-rw-r--r--files/zh-cn/web/api/streams_api/使用可读文件流/index.html307
-rw-r--r--files/zh-cn/web/api/streams_api/概念/index.html118
3 files changed, 570 insertions, 0 deletions
diff --git a/files/zh-cn/web/api/streams_api/index.html b/files/zh-cn/web/api/streams_api/index.html
new file mode 100644
index 0000000000..2fa7ce6e0f
--- /dev/null
+++ b/files/zh-cn/web/api/streams_api/index.html
@@ -0,0 +1,145 @@
+---
+title: Streams API
+slug: Web/API/Streams_API
+translation_of: Web/API/Streams_API
+---
+<div>{{SeeCompatTable}}{{APIRef("Streams")}}</div>
+
+<p class="summary">Streams API允许JavaScript以编程的方式访问通过网络接收的数据流,并根据开发人员的需要处理它们。</p>
+
+<h2 id="概念和用法">概念和用法</h2>
+
+<p>流将你希望通过网络接收的资源拆分成小块,然后按位处理它。这正是浏览器在接收用于显示web页面的资源时做的事情——视频缓冲区和更多的内容可以逐渐播放,有时候随着内容的加载,你可以看到图像逐渐地显示。</p>
+
+<p>但曾经这些对于JavaScript是不可用的。以前,如果我们想要处理某种资源(如视频、文本文件等),我们必须下载完整的文件,等待它反序列化成适当的格式,然后在完整地接收到所有的内容后再进行处理。</p>
+
+<p>随着流在JavaScript中的使用,一切发生了改变——只要原始数据在客户端可用,你就可以使用JavaScript 按位处理它,而不再需要缓冲区、字符串或blob。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15817/Concept.png" style="display: block; height: 382px; margin: 0px auto; width: 1000px;"></p>
+
+<p>还有更多的优点——你可以检测流何时开始或结束,将流链接在一起,根据需要处理错误和取消流,并对流的读取速度做出反应。</p>
+
+<p>流的基础应用围绕着使响应可以被流处理展开。例如,一个成功的 <a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">fetch request</a> 响应 {{domxref("Body")}} 会暴露为 {{domxref("ReadableStream")}},之后你就可以使用 {{domxref("ReadableStream.getReader()")}} 建立的 reader 读取它,使用 {{domxref("ReadableStream.cancel()")}} 取消它等等。</p>
+
+<p>更复杂的应用包括使用 {{domxref("ReadableStream.ReadableStream", "ReadableStream()")}} 创建你自己的流,比如在 <a href="/en-US/docs/Web/API/Service_Worker_API">service worker</a> 中处理数据。</p>
+
+<p>你还可以使用 {{domxref("WritableStream")}} 将数据写入流中。</p>
+
+<div class="note">
+<p><strong>注意:</strong>你可以在这些文章中找到关于流理论的更多细节和实践 — <a href="/en-US/docs/Web/API/Streams_API/Concepts">Streams API concepts</a>, <a href="/en-US/docs/Web/API/Streams_API/Using_readable_streams">Using readable streams</a>,以及 <a href="/en-US/docs/Web/API/Streams_API/Using_writable_streams">Using writable streams</a>。</p>
+</div>
+
+<h2 id="Stream_接口">Stream 接口</h2>
+
+<h3 id="Readable_streams">Readable streams</h3>
+
+<dl>
+ <dt>{{domxref("ReadableStream")}}</dt>
+ <dd>表示数据的可读流。用于处理 <a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a> 返回的响应,或者开发者自定义的流(例如通过 {{domxref("ReadableStream.ReadableStream", "ReadableStream()")}} 构造的流)。</dd>
+ <dt>{{domxref("ReadableStreamDefaultReader")}}</dt>
+ <dd>表示默认阅读器,用于阅读来自网络的数据流(例如 fetch 请求)。</dd>
+ <dt>{{domxref("ReadableStreamDefaultController")}}</dt>
+ <dd>表示控制器,用于控制 {{domxref("ReadableStream")}} 的状态及内部队列。默认的控制器用于处理非字节流。</dd>
+</dl>
+
+<h3 id="Writable_streams">Writable streams</h3>
+
+<dl>
+ <dt>{{domxref("WritableStream")}}</dt>
+ <dd>提供了将流写入目标这个过程的标准抽象表示,称为 sink。内置了背压和队列机制。</dd>
+ <dt>{{domxref("WritableStreamDefaultWriter")}}</dt>
+ <dd>表示默认写入器,用于将小块的数据写入可写流中。</dd>
+ <dt>{{domxref("WritableStreamDefaultController")}}</dt>
+ <dd>表示控制器,用于控制 {{domxref("WritableStream")}} 的状态。当创建一个 <code>WritableStream</code> 时,对应的 <code>WritableStreamDefaultController</code> 实例会被提供给底层的 sink 供其操作。</dd>
+</dl>
+
+<h3 id="流相关的_API_及操作">流相关的 API 及操作</h3>
+
+<dl>
+ <dt>{{domxref("ByteLengthQueuingStrategy")}}</dt>
+ <dd>提供建立流时所需的内置字节队列策略。</dd>
+ <dt>{{domxref("CountQueuingStrategy")}}</dt>
+ <dd>提供建立流时所需的块计数队列策略。</dd>
+</dl>
+
+<h3 id="扩展">扩展</h3>
+
+<dl>
+ <dt>{{domxref("Request")}}</dt>
+ <dd>当构造一个新的 <code>Request</code> 对象后,你可以给它的 <code>RequestInit</code> 中的 <code>body</code> 属性传入一个 {{domxref("ReadableStream")}}。这个 <code>Request</code> 对象就可以被传入 {{domxref("WindowOrWorkerGlobalScope.fetch()")}} 中,开始接收流。</dd>
+ <dt>{{domxref("Body")}}</dt>
+ <dd>一个成功的 <a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">fetch request</a> 响应 {{domxref("Body")}} 会默认暴露为 {{domxref("ReadableStream")}},从而可以采用相应的阅读器来处理等。</dd>
+</dl>
+
+<h3 id="字节流相关接口">字节流相关接口</h3>
+
+<div class="warning">
+<p><strong>重要:</strong>下面的 API 并没有在所有浏览器中都实现,关于规范细节是否处于完成状态可供实现还存在疑问。它们可能随时会改变。</p>
+</div>
+
+<dl>
+ <dt>{{domxref("ReadableStreamBYOBReader")}}</dt>
+ <dd>表示 BYOB("bring your own buffer")阅读器,用于阅读开发者提供的流数据(如自定义的 {{domxref("ReadableStream.ReadableStream", "ReadableStream()")}})。</dd>
+ <dt>{{domxref("ReadableByteStreamController")}}</dt>
+ <dd>表示控制器,用于控制 {{domxref("ReadableStream")}} 的状态及内部队列。字节控制器用于处理字节流。</dd>
+ <dt>{{domxref("ReadableStreamBYOBRequest")}}</dt>
+ <dd>表示 {{domxref("ReadableByteStreamController")}} 中的 BYOB pull request。</dd>
+</dl>
+
+<h2 id="Examples">Examples</h2>
+
+<p>We have created a directory of examples to go along with the Streams API documentation — see <a href="https://github.com/mdn/dom-examples/tree/master/streams">mdn/dom-examples/streams</a>. The examples are as follows:</p>
+
+<ul>
+ <li><a href="http://mdn.github.io/dom-examples/streams/simple-pump/">Simple stream pump</a>: This example shows how to consume a ReadableStream and pass its data to another.</li>
+ <li><a href="http://mdn.github.io/dom-examples/streams/grayscale-png/">Grayscale a PNG</a>: This example shows how a ReadableStream of a PNG can be turned into grayscale.</li>
+ <li><a href="http://mdn.github.io/dom-examples/streams/simple-random-stream/">Simple random stream</a>: This example shows how to use a custom stream to generate random strings, enqueue them as chunks, and then read them back out again.</li>
+ <li><a href="http://mdn.github.io/dom-examples/streams/simple-tee-example/">Simple tee example</a>: This example extends the Simple random stream example, showing how a stream can be teed and both resulting streams can be read independently.</li>
+ <li><a href="http://mdn.github.io/dom-examples/streams/simple-writer/">Simple writer</a>: This example shows how to to write to a writable stream, then decode the stream and write the contents to the UI.</li>
+ <li><a href="http://mdn.github.io/dom-examples/streams/png-transform-stream/">Unpack chunks of a PNG</a>: This example shows how <a href="https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeThrough"><code>pipeThrough()</code></a> can be used to transform a ReadableStream into a stream of other data types by transforming a data of a PNG file into a stream of PNG chunks.</li>
+</ul>
+
+<p>Examples from other developers:</p>
+
+<ul>
+ <li><a href="https://fetch-progress.anthum.com/">Progress Indicators with Streams, Service Workers, &amp; Fetch</a>.</li>
+</ul>
+
+<h2 id="Specifications">Specifications</h2>
+
+<table class="standard-table">
+ <tbody>
+ <tr>
+ <th scope="col">Specification</th>
+ <th scope="col">Status</th>
+ <th scope="col">Comment</th>
+ </tr>
+ <tr>
+ <td>{{SpecName('Streams')}}</td>
+ <td>{{Spec2('Streams')}}</td>
+ <td>Initial definition.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Browser_compatibility">Browser compatibility</h2>
+
+<div class="hidden">
+<p>The compatibility table in this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</p>
+</div>
+
+<h3 id="ReadableStream">ReadableStream</h3>
+
+<p>{{Compat("api.ReadableStream")}}</p>
+
+<h3 id="WritableStream">WritableStream</h3>
+
+<p>{{Compat("api.WritableStream")}}</p>
+
+<h2 id="See_also">See also</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/API/Streams_API/Concepts">Streams API concepts</a></li>
+ <li><a href="/en-US/docs/Web/API/Streams_API/Using_readable_streams">Using readable streams</a></li>
+ <li><a href="/en-US/docs/Web/API/Streams_API/Using_writable_streams">Using writable streams</a></li>
+</ul>
diff --git a/files/zh-cn/web/api/streams_api/使用可读文件流/index.html b/files/zh-cn/web/api/streams_api/使用可读文件流/index.html
new file mode 100644
index 0000000000..5313b80dc2
--- /dev/null
+++ b/files/zh-cn/web/api/streams_api/使用可读文件流/index.html
@@ -0,0 +1,307 @@
+---
+title: 使用可读文件流
+slug: Web/API/Streams_API/使用可读文件流
+translation_of: Web/API/Streams_API/Using_readable_streams
+---
+<div>{{apiref("Streams")}}</div>
+
+<p class="summary">作为一个js开发者,一块一块的读取和操作一个从网上获取的数据流是非常实用的功能!但是如何使用Streams API操作数据流呢? 可以在这里看到基本的介绍.</p>
+
+<div class="note">
+<p><strong>提示</strong>: 本文要求您已了解流文件相关知识,如果还不了解,建议您先查看 <a href="/en-US/docs/Web/API/Streams_API#Concepts_and_usage">文件流概念及简介</a> 以及 dedicated <a href="/en-US/docs/Web/API/Streams_API/Concepts">文件流API</a> 然后再阅读此文.</p>
+</div>
+
+<div class="note">
+<p><strong>提示</strong>: 如果你正在查询关于可读写的文件流, 请查看<a href="/en-US/docs/Web/API/Streams_API/Using_writable_streams">使用可写文件流</a> .</p>
+</div>
+
+<h2 id="Browser_support">Browser support</h2>
+
+<p>You can consume Fetch {{domxref("Body")}} objects as streams and create your own custom readable streams in Firefox 65+ and Chrome 42+ (and equivalent Chromium-based browsers). <a href="/en-US/docs/Web/API/Streams_API/Concepts#Pipe_chains">Pipe chains</a> are only supported in Chrome at the moment, and that functionality is subject to change.</p>
+
+<h2 id="Finding_some_examples">Finding some examples</h2>
+
+<p>We will look at various examples in this article, taken from our <a href="https://github.com/mdn/dom-examples/tree/master/streams">dom-examples/streams</a> repo. You can find the full source code there, as well as links to the examples.</p>
+
+<h2 id="Consuming_a_fetch_as_a_stream">Consuming a fetch as a stream</h2>
+
+<p>The <a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a> allows you to fetch resources across the network, providing a modern alternative to <a href="/en-US/docs/User%3Amaybe/webidl_mdn/XMLHttpRequest">XHR</a>. It has a number of advantages, and what is really nice about it is that browsers have recently added the ability to consume a fetch response as a readable stream.</p>
+
+<p>The {{domxref("Body")}} mixin now includes the {{domxref("Body.body","body")}} property, which is a simple getter exposing the body contents as a readable stream. This mixin is implemented by both the {{domxref("Request")}} and {{domxref("Response")}} interfaces, so it is available on both, although consuming the stream of a response body is perhaps a bit more obvious.</p>
+
+<p>As our <a href="https://github.com/mdn/dom-examples/tree/master/streams/simple-pump">Simple stream pump</a> example shows (<a href="https://mdn.github.io/dom-examples/streams/simple-pump/">see it live also</a>), exposing it is a matter of just accessing the <code>body</code> property of the response:</p>
+
+<pre class="brush: js">// Fetch the original image
+fetch('./tortoise.png')
+// Retrieve its body as ReadableStream
+.then(response =&gt; response.body)</pre>
+
+<p>This provides us with a {{domxref("ReadableStream")}} object.</p>
+
+<h3 id="Attaching_a_reader">Attaching a reader</h3>
+
+<p>Now we’ve got our streaming body, reading the stream requires attaching a reader to it. This is done using the {{domxref("ReadableStream.getReader()")}} method:</p>
+
+<pre class="brush: js">// Fetch the original image
+fetch('./tortoise.png')
+// Retrieve its body as ReadableStream
+.then(response =&gt; response.body)
+.then(body =&gt; {
+ const reader = body.getReader();</pre>
+
+<p>Invoking this method creates a reader and locks it to the stream — no other reader may read this stream until this reader is released, e.g. by invoking {{domxref("ReadableStreamDefaultReader.releaseLock()")}}.</p>
+
+<p>Also note that the previous example can be reduced by one step, as <code>response.body</code> is synchronous and so doesn't need the promise:</p>
+
+<pre class="brush: js">// Fetch the original image
+ fetch('./tortoise.png')
+ // Retrieve its body as ReadableStream
+ .then(response =&gt; {
+ const reader = response.body.getReader();</pre>
+
+<h3 id="Reading_the_stream">Reading the stream</h3>
+
+<p>Now you’ve got your reader attached, you can read data chunks out of the stream using the {{domxref("ReadableStreamDefaultReader.read()")}} method. This reads one chunk out of the stream, which you can then do anything you like with. For example, our Simple stream pump example goes on to enqueue each chunk in a new, custom <code>ReadableStream</code> (we will find more about this in the next section), then create a new {{domxref("Response")}} out of it, consume it as a {{domxref("Blob")}}, create an object URL out of that blob using {{domxref("URL.createObjectURL()")}}, and then display it on screen in an {{htmlelement("img")}} element, effectively creating a copy of the image we originally fetched.</p>
+
+<pre class="brush: js"> return new ReadableStream({
+ start(controller) {
+ return pump();
+ function pump() {
+ return reader.read().then(({ done, value }) =&gt; {
+ // When no more data needs to be consumed, close the stream
+ if (done) {
+ controller.close();
+ return;
+ }
+ // Enqueue the next data chunk into our target stream
+ controller.enqueue(value);
+ return pump();
+ });
+ }
+ }
+ })
+})
+.then(stream =&gt; new Response(stream))
+.then(response =&gt; response.blob())
+.then(blob =&gt; URL.createObjectURL(blob))
+.then(url =&gt; console.log(image.src = url))
+.catch(err =&gt; console.error(err));</pre>
+
+<p>Let’s look in detail at how <code>read()</code> is used. In the <code>pump()</code> function seen above we first invoke <code>read()</code>, which returns a promise containing a results object — this has the results of our read in it, in the form <code>{ done, value }</code>:</p>
+
+<pre class="brush: js">return reader.read().then(({ done, value }) =&gt; {</pre>
+
+<p>The results can be one of three different types:</p>
+
+<ul>
+ <li>If a chunk is available to read, the promise will be fulfilled with an object of the form { value: theChunk, done: false }.</li>
+ <li>If the stream becomes closed, the promise will be fulfilled with an object of the form { value: undefined, done: true }.</li>
+ <li>If the stream becomes errored, the promise will be rejected with the relevant error.</li>
+</ul>
+
+<p>Next, we check whether done is <code>true</code>. If so, there are no more chunks to read (the value is <code>undefined</code>) so we return out of the function and close the custom stream with {{domxref("ReadableStreamDefaultController.close()")}}:</p>
+
+<pre class="brush: js">if (done) {
+ controller.close();
+ return;
+}</pre>
+
+<div class="note">
+<p><strong>Note</strong>: <code>close()</code> is part of the new custom stream, not the original stream we are discussing here. We’ll explain more about the custom stream in the next section.</p>
+</div>
+
+<p>If <code>done</code> is not true, we process the new chunk we’ve read (contained in the <code>value</code> property of the results object) and then call the <code>pump()</code> function again to read the next chunk.</p>
+
+<pre class="brush: js">// Enqueue the next data chunk into our target stream
+controller.enqueue(value);
+return pump();</pre>
+
+<p>This is the standard pattern you’ll see when using stream readers:</p>
+
+<ol>
+ <li>You write a function that starts off by reading the stream.</li>
+ <li>If there is no more stream to read, you return out of the function.</li>
+ <li>If there is more stream to read, you process the current chunk then run the function again.</li>
+ <li>You keep running the function recursively until there is no more stream to read, in which case step 2 is followed.</li>
+</ol>
+
+<h2 id="Creating_your_own_custom_readable_stream">Creating your own custom readable stream</h2>
+
+<p>The Simple stream pump example we’ve been studying throughout this article includes a second part — once we’ve read the image from the fetch body in chunks, we then enqueue them into another, custom stream of our own creation. How do we create this? The <code>ReadableStream</code> constructor.</p>
+
+<h3 id="The_ReadableStream_constructor">The ReadableStream constructor</h3>
+
+<p>It is easy to read from a stream when the browser provides it for you as in the case of Fetch, but sometimes you need to create a custom stream and populate it with your own chunks. The {{domxref("ReadableStream.ReadableStream()")}} constructor allows you to do this via a syntax that looks complex at first, but actually isn’t too bad.</p>
+
+<p>The generic syntax skeleton looks like this:</p>
+
+<pre class="brush: js">const stream = new ReadableStream({
+ start(controller) {
+
+ },
+ pull(controller) {
+
+ },
+ cancel() {
+
+ },
+ type,
+ autoAllocateChunkSize
+}, {
+ highWaterMark,
+ size()
+});</pre>
+
+<p>The constructor takes two objects as parameters. The first object is required, and creates a model in JavaScript of the underlying source the data is being read from. The second object is optional, and allows you to specify a <a href="/en-US/docs/Web/API/Streams_API/Concepts#Internal_queues_and_queuing_strategies">custom queueing strategy</a> to use for your stream. You’ll rarely have to do this, so we’ll just concentrate on the first one for now.</p>
+
+<p>The first object can contain up to five members, only the first of which is required:</p>
+
+<ol>
+ <li><code>start(controller)</code> — A method that is called once, immediately after the <code>ReadableStream</code> is constructed. Inside this method, you should include code that sets up the stream functionality, e.g. beginning generation of data or otherwise getting access to the source.</li>
+ <li><code>pull(controller)</code> — A method that, when included, is called repeatedly until the stream’s internal queue is full. This can be used to control the stream as more chunks are enqueued.</li>
+ <li><code>cancel()</code> — A method that, when included, will be called if the app signals that the stream is to be cancelled (e.g. if {{domxref("ReadableStream.cancel()")}} is called). The contents should do whatever is necessary to release access to the stream source.</li>
+ <li><code>type</code> and <code>autoAllocateChunkSize</code> — These are used — when included — to signify that the stream is to be a bytestream. Bytestreams will be covered separately in a future tutorial, as they are somewhat different in purpose and use case to regular (default) streams. They are also not implemented anywhere as yet.</li>
+</ol>
+
+<p>Looking at our simple example code again, you can see that our <code>ReadableStream</code> constructor only includes a single method — <code>start()</code>, which serves to read all the data out of our fetch stream.</p>
+
+<pre class="brush: js"> return new ReadableStream({
+ start(controller) {
+ return pump();
+ function pump() {
+ return reader.read().then(({ done, value }) =&gt; {
+ // When no more data needs to be consumed, close the stream
+ if (done) {
+ controller.close();
+ return;
+ }
+ // Enqueue the next data chunk into our target stream
+ controller.enqueue(value);
+ return pump();
+ });
+ }
+ }
+ })
+})
+</pre>
+
+<h3 id="ReadableStream_controllers">ReadableStream controllers</h3>
+
+<p>You’ll notice that the <code>start()</code> and <code>pull()</code> methods passed into the <code>ReadableStream</code> constructor are given <code>controller</code> parameters — these are instances of the {{domxref("ReadableStreamDefaultController")}} class, which can be used to control your stream.</p>
+
+<p>In our example we are using the controller’s {{domxref("ReadableStreamDefaultController.enqueue","enqueue()")}} method to enqueue a value into the custom stream after it is read from the fetch body.</p>
+
+<p>In addition, when we are done reading the fetch body we use the controller’s {{domxref("ReadableStreamDefaultController.close","close()")}} method to close the custom stream — any previously-enqueued chunks can still be read from it, but no more can be enqueued, and the stream is closed when reading has finished.</p>
+
+<p>Reading from custom streams</p>
+
+<p>In our Simple stream pump example, we consume the custom readable stream by passing it into a {{domxref("Response.Response", "Response")}} constructor call, after which we consume it as a blob().</p>
+
+<pre class="brush: js">.then(stream =&gt; new Response(stream))
+.then(response =&gt; response.blob())
+.then(blob =&gt; URL.createObjectURL(blob))
+.then(url =&gt; console.log(image.src = url))
+.catch(err =&gt; console.error(err));</pre>
+
+<p>But a custom stream is still a <code>ReadableStream</code> instance, meaning you can attach a reader to it. As an example, have a look at our <a href="https://github.com/mdn/dom-examples/blob/master/streams/simple-random-stream/index.html">Simple random stream demo</a> (<a href="https://mdn.github.io/dom-examples/streams/simple-random-stream/">see it live also</a>), which creates a custom stream, enqueues some random strings into it, and then reads the data out of the stream again once the <em>Stop string generation</em> button is pressed.</p>
+
+<p>The custom stream constructor has a <code>start()</code> method that uses a {{domxref("WindowTimers.setInterval()")}} call to generate a random string every second. {{domxref("ReadableStreamDefaultController.enqueue()")}} is then used to enqueue it into the stream. When the button is pressed, the interval is cancelled, and a function called <code>readStream()</code> is invoked to read the data back out of the stream again. We also close the stream, as we’ve stopped enqueueing chunks to it.</p>
+
+<pre class="brush: js">const stream = new ReadableStream({
+ start(controller) {
+ interval = setInterval(() =&gt; {
+ let string = randomChars();
+ // Add the string to the stream
+ controller.enqueue(string);
+ // show it on the screen
+ let listItem = document.createElement('li');
+ listItem.textContent = string;
+ list1.appendChild(listItem);
+ }, 1000);
+ button.addEventListener('click', function() {
+ clearInterval(interval);
+ readStream();
+ controller.close();
+ })
+ },
+ pull(controller) {
+ // We don't really need a pull in this example
+ },
+ cancel() {
+ // This is called if the reader cancels,
+ // so we should stop generating strings
+ clearInterval(interval);
+ }
+});</pre>
+
+<p>In the <code>readStream()</code> function itself, we lock a reader to the stream using {{domxref("ReadableStream.getReader()")}}, then follow the same kind of pattern we saw earlier — reading each chunk with <code>read()</code>, checking whether <code>done</code> is <code>true</code> and then ending the process if so, and reading the next chunk and processing it if not, before running the <code>read()</code> function again.</p>
+
+<pre class="brush: js">function readStream() {
+ const reader = stream.getReader();
+ let charsReceived = 0;
+
+ // read() returns a promise that resolves
+ // when a value has been received
+ reader.read().then(function processText({ done, value }) {
+ // Result objects contain two properties:
+ // done - true if the stream has already given you all its data.
+ // value - some data. Always undefined when done is true.
+ if (done) {
+ console.log("Stream complete");
+ para.textContent = result;
+ return;
+ }
+
+ charsReceived += value.length;
+ const chunk = value;
+ let listItem = document.createElement('li');
+ listItem.textContent = 'Read ' + charsReceived + ' characters so far. Current chunk = ' + chunk;
+ list2.appendChild(listItem);
+
+ result += chunk;
+
+ // Read some more, and call this function again
+ return reader.read().then(processText);
+ });
+}</pre>
+
+<h3 id="Closing_and_cancelling_streams">Closing and cancelling streams</h3>
+
+<p>We’ve already shown examples of using {{domxref("ReadableStreamDefaultController.close()")}} to close a reader. As we said before, any previously enqueued chunks will still be read, but no more can be enqueued because it is closed.</p>
+
+<p>If you wanted to completely get rid of the stream and discard any enqueued chunks, you'd use {{domxref("ReadableStream.cancel()")}} or {{domxref("ReadableStreamDefaultReader.cancel()")}}.</p>
+
+<h2 id="Teeing_a_stream">Teeing a stream</h2>
+
+<p>Sometimes you might want to read a stream twice, simultaneously. This is achieved via the {{domxref("ReadableStream.tee()")}} method — it outputs an array containing two identical copies of the original readable stream, which can then be read independently by two separate readers.</p>
+
+<p>You might do this for example in a <a href="/en-US/docs/Web/API/Service_Worker_API">ServiceWorker</a> if you want to fetch a response from the server and stream it to the browser, but also stream it to the Service Worker cache. Since a response body cannot be consumed more than once, and a stream can't be read by more than one reader at once, you’d need two copies to do this.</p>
+
+<p>We provide an example of this in our <a href="https://github.com/mdn/dom-examples/blob/master/streams/simple-tee-example/index.html">Simple tee example</a> (<a href="https://mdn.github.io/dom-examples/streams/simple-tee-example/">see it live also</a>). This example works much the same way as our Simple random stream, except that when the button is pressed to stop generating random strings, the custom stream is taken and teed, and both resulting streams are then read:</p>
+
+<pre class="brush: js">function teeStream() {
+ const teedOff = stream.tee();
+ readStream(teedOff[0], list2);
+ readStream(teedOff[1], list3);
+ }</pre>
+
+<h2 id="Pipe_chains">Pipe chains</h2>
+
+<p>One very experimental feature of streams is the ability to pipe streams into one another (called a <a href="/en-US/docs/Web/API/Streams_API/Concepts#Pipe_chains">pipe chain</a>). This involves two methods — {{domxref("ReadableStream.pipeThrough()")}}, which pipes a readable stream through a writer/reader pair to transform one data format into another, and {{domxref("ReadableStream.pipeTo()")}}, which pipes a readable stream to a writer acting as an end point for the pipe chain.</p>
+
+<p>This functionality is at a very experimental stage and is subject to change, so we have no explored it too deeply as of yet.</p>
+
+<p>We have created an example called <a href="https://github.com/mdn/dom-examples/tree/master/streams/png-transform-stream">Unpack Chunks of a PNG</a> (<a href="https://mdn.github.io/dom-examples/streams/png-transform-stream/">see it live also</a>) that fetches an image as a stream, then pipes it through to a custom PNG transform stream <span class="pl-c">that retrieves PNG chunks out of a binary data stream.</span></p>
+
+<pre class="brush: js">// Fetch the original image
+fetch('png-logo.png')
+// Retrieve its body as ReadableStream
+.then(response =&gt; response.body)
+// Create a gray-scaled PNG stream out of the original
+.then(rs =&gt; logReadableStream('Fetch Response Stream', rs))
+.then(body =&gt; body.pipeThrough(new PNGTransformStream()))
+.then(rs =&gt; logReadableStream('PNG Chunk Stream', rs))</pre>
+
+<h2 id="Summary">Summary</h2>
+
+<p>That explains the basics of “default” readable streams. We’ll explain bytestreams in a separate future article, once they are available in browsers.</p>
diff --git a/files/zh-cn/web/api/streams_api/概念/index.html b/files/zh-cn/web/api/streams_api/概念/index.html
new file mode 100644
index 0000000000..9c2d9f77ae
--- /dev/null
+++ b/files/zh-cn/web/api/streams_api/概念/index.html
@@ -0,0 +1,118 @@
+---
+title: Streams API 概念
+slug: Web/API/Streams_API/概念
+tags:
+ - 概念
+ - 流
+translation_of: Web/API/Streams_API/Concepts
+---
+<div>{{apiref("Streams")}}</div>
+
+<p class="summary"><a href="/en-US/docs/Web/API/Streams_API">Streams API</a> 为 Web 平台提供了一组十分有用的工具,提供了一系列对象以允许 JavaScript 访问来自网络的数据流,并根据开发人员的需要对其进行处理。与流相关的一些概念和术语可能会令您感到陌生——本文将解释您需要了解的所有内容。</p>
+
+<h2 id="Readable_streams">Readable streams</h2>
+
+<p>一个可读流是一个数据源,在 JavaScript 中用一个 {{domxref("ReadableStream")}} 对象表示,数据从它的 <strong>underlying source (底层源)</strong> 流出 —— 底层源表示一个您希望从中获取数据的,来自网络或其他域的,某种资源。</p>
+
+<p>有两种 underlying source:</p>
+
+<ul>
+ <li><strong>Push sources</strong> 会在您访问了它们之后,不断地主动推送数据。您可以自行 start, pause, 或 cancel 对流的访问。例如视频流和 TCP/<a href="/en-US/docs/Web/API/WebSockets_API">Web sockets</a> 。</li>
+ <li><strong>Pull sources</strong> 需要您在连接到它们后,显式地从它们请求数据。例如通过 <a href="/en-US/docs/Web/API/Fetch_API">Fetch</a> 或 <a href="/en-US/docs/Web/API/XMLHttpRequest/XMLHttpRequest">XHR</a> 请求访问一个文件。</li>
+</ul>
+
+<p>数据被按序读入到许多小块,这些小块被称作 <strong>chunk</strong>。<strong>chunk</strong> 可以是单个字节,也可以是某种更大的数据类型,例如特定大小的 <a href="https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays">typed array</a> 。来自一个流的 <strong>chunks</strong> 可以有不同的大小和类型。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15819/Readable%20streams.png" style="height: 452px; width: 1000px;"></p>
+
+<p>已放入到流中的 chunks 称作 <strong>enqueued</strong> —— 这意味着它们已经准备好被读取,并等待在队列中。流的一个 <strong>internal queue</strong> 跟踪了那些尚未读取的块(请参阅下面的<strong>内部队列和队列策略</strong>部分)。</p>
+
+<p>流中的 chunks 由一个 <strong>reader</strong> 读取 —— 该数据处理过程一次只处理一个 chunk,允许您对其执行任何类型的操作。reader 加上与它一起运行的其他处理代码合称为一个 <strong>consumer </strong>。</p>
+
+<p>另一个您将用到的对象叫做 <strong>controller</strong> —— 每个 reader 都有一个关联的 controller,用来控制流 (例如,可以将流 close)。</p>
+
+<p>一个流一次只能被一个 reader 读;当一个 reader 被创建并开始读一个流(an <strong>active reader</strong>),我们说,它被 <strong>locked</strong> 在该流上。如果您想让另一个 reader 读这个流,则通常需要先取消第一个 reader ,再执行其他操作。(您可以 <strong>tee</strong> 流,参阅下面的 Teeing 部分)</p>
+
+<p>注意,有两种不同类型的可读流。除了传统的可读流之外,还有一种类型叫做字节流 —— 这是传统流的扩展版本,用于读取底层字节源。相比可读流,字节流除了 <strong><dfn>default reader</dfn></strong> ,还可以使用 <strong><dfn>BYOB reader </dfn></strong><dfn>(BYOB, or "bring your own buffer", "带上你自己的缓冲") 。</dfn>这种 reader 可以直接将流读入开发者提供的缓冲区,从而最大限度地减少所需的复制。您的代码将使用哪种底层流(以及使用哪种 reader 和 controller)取决于流最初是如何创建的(请参阅{{domxref("ReadableStream.ReadableStream","ReadableStream()")}} 构造函数页面)。</p>
+
+<div class="warning">
+<p><strong>Important</strong>: 字节流尚未在任何地方实现,并且规范的细节被质疑是否处于可以实现的高度完成的状态。这种情况可能会随着时间而变化。</p>
+</div>
+
+<p>您可以使用现成的可读流,例如来自 fetch 请求的 {{domxref("Response.body")}} ,也可以使用 {{domxref("ReadableStream.ReadableStream","ReadableStream()")}} constructor 生成您自定义的流。</p>
+
+<h2 id="Teeing">Teeing</h2>
+
+<p>尽管同一时刻只能有一个 reader 可以读取流,我们可以把流分割成两个相同的副本,这样它们就可以用两个独立的 reader 读取。该过程称为 <strong>teeing </strong>。</p>
+
+<p>在 JavaScript 中,该过程由调用 {{domxref("ReadableStream.tee()")}} 实现 —— 它返回一个数组,包含对原始可读流的两个相同的副本可读流,可以独立地被不同的 reader 读取。</p>
+
+<p>举例而言,您在 <a href="/en-US/docs/Web/API/Service_Worker_API">ServiceWorker</a> 中可能会用到该方法,当您从服务器 fetch 资源,得到一个响应的可读流,您可能会想把这个流拆分成两个,一个流入到浏览器,另一个流入到 ServiceWorker 的缓存。由于 response 的 body 无法被消费两次,以及可读流无法被两个 reader 同时读取,您会需要两个可读流副本来实现需求。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15820/tee.png" style="height: 527px; width: 1000px;"></p>
+
+<h2 id="Writable_streams">Writable streams</h2>
+
+<p>一个 <strong>Writable stream (可写流) </strong>是一个可以写入数据的数据终点,在 JavaScript 中以一个 {{domxref("WritableStream")}} 对象表示。这是 JavaScript 层面对 <strong>underlying sink (底层汇) </strong>的抽象 —— 底层汇是某个可以写入原始数据的更低层次的 I/O 数据汇。</p>
+
+<p>数据由一个 <strong>writer </strong>写入流中,每次一个 chunk 。chunk 和可读流的 reader 一样可以有多种类型。您可以用任何方式生成要被写入的块;writer 加上相关的代码称为 <strong>producer</strong>。</p>
+
+<p>When a writer is created and starts writing to a stream (an <strong>active writer</strong>), it is said to be <strong>locked</strong> to it. Only one writer can write to a writable stream at one time. If you want another writer to start writing to your stream, you typically need to abort it before you then attach another writer to it.</p>
+
+<p>An <strong>internal queue</strong> keeps track of the chunks that have been written to the stream but not yet been processed by the underlying sink.</p>
+
+<p>There is also a construct you’ll use called a controller — each writer has an associated controller that allows you to control the stream (for example, to abort it if wished).</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15821/writable%20streams.png" style="height: 452px; width: 1000px;"></p>
+
+<p>You can make use of writable streams using the {{domxref("WritableStream.WritableStream","WritableStream()")}} constructor. These currently have very limited availability in browsers.</p>
+
+<h2 id="Pipe_chains">Pipe chains</h2>
+
+<p>The Streams API makes it possible to pipe streams into one another (or at least it will do when browsers implement the relevant functionality) using a structure called a <strong>pipe chain</strong>. There are two methods available in the spec to facilitate this:</p>
+
+<ul>
+ <li>{{domxref("ReadableStream.pipeThrough()")}} — pipes the stream through a <strong>transform stream</strong>, which is a pair comprised of a writable stream that has data written to it, and a readable stream that then has the data read out of it — this acts as a kind of treadmill that constantly takes data in and transforms it to a new state. Effectively the same stream is written to, and then the same values are read. A simple example is a text decoder, where raw bytes are written, and then strings are read. You can find more useful ideas and examples in the spec — see <a href="https://streams.spec.whatwg.org/#ts-model">Transform streams</a> for ideas, and <a href="https://streams.spec.whatwg.org/#example-both">this web sockets example</a>.</li>
+ <li>{{domxref("ReadableStream.pipeTo()")}} — pipes to a writable stream that acts as the end point of the pipe chain.</li>
+</ul>
+
+<p>The start of the pipe chain is called the <strong>original source</strong>, and the end is called the <strong>ultimate sink</strong>.</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15818/PipeChain.png" style="height: 382px; width: 1000px;"></p>
+
+<div class="note">
+<p><strong>Note</strong>: This functionality isn't fully thought through yet, or available in many browsers. At some point the spec writers hope to add something like a <code>TransformStream</code> class to make creating transform streams easier.</p>
+</div>
+
+<h2 id="Backpressure">Backpressure</h2>
+
+<p>流的一个重要概念是 <strong>backpressure (背压)</strong> —— 这是单个流或一个 pipe chain 调节读/写速度的过程。当链中后面的一个流仍然忙碌,尚未准备好接受更多的 chunks 时,它会通过链向上游的流发送一个信号,告诉上游的转换流(或原始源)适当地减慢传输速度,这样您就不会在任何地方遇到瓶颈。</p>
+
+<p>要在可读流中使用 backpressure 技术,我们可以通过查询 controller 的 {{domxref("ReadableStreamDefaultController.desiredSize")}} 属性。如果该值太低或为负数,我们的 ReadableStream 可以告诉它的底层源停止往流中装载数据,然后我们沿着 stream chain 进行背压。</p>
+
+<p>可读流在经历背压后,如果 consumer 再次想要接收数据,我们可以在构造可读流时提供 pull 方法来告诉底层源恢复往流中装载数据。</p>
+
+<h2 id="Internal_queues_and_queuing_strategies">Internal queues and queuing strategies</h2>
+
+<p>As mentioned earlier, the chunks in a stream that have not yet been processed and finished with are kept track of by an internal queue.</p>
+
+<ul>
+ <li>In the case of readable streams, these are the chunks that have been enqueued but not yet read</li>
+ <li>In the case of writable streams, these are chunks that have been written but not yet processed by the underlying sink.</li>
+</ul>
+
+<p>Internal queues employ a <strong>queuing strategy</strong>, which dictates how to signal backpressure based on the <strong>internal queue state.</strong></p>
+
+<p>In general, the strategy compares the size of the chunks in the queue to a value called the <strong>high water mark</strong>, which is the largest total chunk size that the queue can realistically manage.</p>
+
+<p>The calculation performed is</p>
+
+<pre class="notranslate">high water mark - total size of chunks in queue = desired size</pre>
+
+<p>The <strong>desired size</strong> is the size of chunks the stream can still accept to keep the stream flowing but below the high water mark in size. After the calculation is performed, chunk generation will be slowed down/sped up as appropriate to keep the stream flowing as fast as possible while keeping the desired size above zero. If the value falls to zero (or below in the case of writable streams), it means that chunks are being generated faster than the stream can cope with, which results in problems.</p>
+
+<div class="note">
+<p><strong>Note</strong>: What happens in the case of zero or negative desired size hasn’t really been defined in the spec so far. Patience is a virtue.</p>
+</div>
+
+<p>As an example, let's take a chunk size of 1, and a high water mark of 3. This means that up to 3 chunks can be enqueued before the high water mark is reached and backpressure is applied.</p>