Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions files/en-us/web/api/readablestream/tee/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,29 @@ The **`tee()`** method of the
two-element array containing the two resulting branches as
new {{domxref("ReadableStream")}} instances.

This is useful for allowing two readers to read a stream simultaneously, perhaps at
different speeds. You might do this for example in a ServiceWorker if you want to fetch
This is useful for allowing two readers to read a stream sequentially or simultaneously,
perhaps at different speeds.
For example, you might do this in a ServiceWorker if you want to fetch
a response from the server and stream it to the browser, but also stream it to the
ServiceWorker cache. Since a response body cannot be consumed more than once, you'd need
two copies to do this.

A teed stream will partially signal backpressure at the rate of the *faster* consumer
of the two `ReadableStream` branches,
and unread data is enqueued internally on the slower consumed `ReadableStream`
without any limit or backpressure.
That is, when *both* branches have an unread element in their internal queue,
then the original `ReadableStream`’s controller’s queue will start to fill up,
and once its {{domxref("ReadableStreamDefaultController.desiredSize", "desiredSize")}} ≤ 0
or byte stream controller {{domxref("ReadableByteStreamController.desiredSize", "desiredSize")}} ≤ 0,
then the controller will stop calling `pull(controller)` on the
underlying source passed to {{domxref("ReadableStream.ReadableStream", "new ReadableStream()")}}.
If only one branch is consumed, then the entire body will be enqueued in memory.
Therefore, you should not use the built-in `tee()` to read very large streams
in parallel at different speeds.
Instead, search for an implementation that fully backpressures
to the speed of the *slower* consumed branch.

To cancel the stream you then need to cancel both resulting branches. Teeing a stream
will generally lock it for the duration, preventing other readers from locking it.

Expand Down
7 changes: 7 additions & 0 deletions files/en-us/web/api/request/clone/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ browser-compat: api.Request.clone

The **`clone()`** method of the {{domxref("Request")}} interface creates a copy of the current `Request` object.

Like the underlying {{domxref("ReadableStream.tee")}} api,
the {{domxref("Request.body", "body")}} of a cloned `Response`
will signal backpressure at the rate of the *faster* consumer of the two bodies,
and unread data is enqueued internally on the slower consumed `body`
without any limit or backpressure.
Beware when you construct a `Request` from a stream and then `clone` it.

`clone()` throws a {{jsxref("TypeError")}} if the request body has already been used. In fact, the main reason `clone()` exists is to allow multiple uses of body objects (when they are one-use only.)

If you intend to modify the request, you may prefer the {{domxref("Request")}} constructor.
Expand Down
15 changes: 15 additions & 0 deletions files/en-us/web/api/response/clone/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ browser-compat: api.Response.clone

The **`clone()`** method of the {{domxref("Response")}} interface creates a clone of a response object, identical in every way, but stored in a different variable.

Like the underlying {{domxref("ReadableStream.tee")}} api,
the {{domxref("Response.body", "body")}} of a cloned `Response`
will signal backpressure at the rate of the *faster* consumer of the two bodies,
and unread data is enqueued internally on the slower consumed `body`
without any limit or backpressure.
Backpressure refers to the mechanism by which the streaming consumer of data
(in this case, the code that reads the body)
slows down the producer of data (such as the TCP server)
so as not to load large amounts of data in memory
that is waiting to be used by the application.
If only one cloned branch is consumed, then the entire body will be buffered in memory.
Therefore, `clone()` is one way to read a response twice in sequence,
but you should not use it to read very large bodies
in parallel at different speeds.

`clone()` throws a {{jsxref("TypeError")}} if the response body has already been used.
In fact, the main reason `clone()` exists is to allow multiple uses of body objects (when they are one-use only.)

Expand Down