Skip to content

Commit 7e10304

Browse files
committed
Add section on other stream pairs
1 parent 6961f9e commit 7e10304

File tree

1 file changed

+76
-1
lines changed

1 file changed

+76
-1
lines changed

index.bs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6402,7 +6402,7 @@ reason.
64026402
1. Perform ! [$SetUpTransformStreamDefaultController$](|stream|, |controller|,
64036403
|transformAlgorithmWrapper|, |flushAlgorithmWrapper|).
64046404
1. Return |stream|.
6405-
</dfn>
6405+
</div>
64066406

64076407
<p algorithm>To <dfn export lt="create an identity TransformStream|creating an identity
64086408
TransformStream">create an identity {{TransformStream}}</dfn>, return the result of
@@ -6473,6 +6473,81 @@ base {{TransformStream}} class provides. The most common driver for such a wrapp
64736473
custom [=constructor operation=], but if your conceptual transform stream isn't meant to be
64746474
constructed, then using {{TransformStream}} directly is fine.
64756475

6476+
<h3 id="other-specs-pairs">Other stream pairs</h3>
6477+
6478+
Apart from [=transform streams=], discussed above, specifications often create pairs of [=readable
6479+
stream|readable=] and [=writable stream|writable=] streams. This section gives some guidance for
6480+
such situations.
6481+
6482+
In all such cases, specifications should use the names `readable` and `writable` for the two
6483+
properties exposing the streams in question. They should not use other names (such as
6484+
`input`/`output` or `readableStream`/`writableStream`), and they should not use methods or other
6485+
non-property means of access to the streams.
6486+
6487+
<h4 id="other-specs-duplex">Duplex streams</h4>
6488+
6489+
The most common readable/writable pair is a <dfn export>duplex stream</dfn>, where the readable and
6490+
writable streams represent two sides of a single shared resource, such as a socket, connection, or
6491+
device.
6492+
6493+
The trickiest thing to consider when specifying duplex streams is how to handle operations like
6494+
[=cancel a readable stream|canceling=] the readable side, or closing or [=abort a writable
6495+
stream|aborting=] the writable side. It might make sense to leave duplex streams "half open", with
6496+
such operations one one side not impacting the other side. Or it might be best to carry over their
6497+
effects to the other side, e.g. by specifying that your readable side's
6498+
<var ignore>[=ReadableStream/create/cancelAlgorithm=]</var> will [=WritableStream/close=] the
6499+
writable side.
6500+
6501+
<p class="example" id="example-basic-duplex">A basic example of a duplex stream, created through
6502+
JavaScript instead of through specification prose, is found in [[#example-both]]. It illustrates
6503+
this carry-over behavior.
6504+
6505+
Another consideration is how to handle the creation of duplex streams which need to be acquired
6506+
asynchronously, e.g. via establishing a connection. The preferred pattern here is to have a
6507+
constructible class with a promise-returning property that fulfills with the actual duplex stream
6508+
object. That duplex stream object can also then expose any information that is only available
6509+
asynchronously, e.g. connection data. The container class can then provide convenience APIs, such as
6510+
a function to close the entire connection instead of only closing individual sides.
6511+
6512+
<p class="example" id="example-duplex-with-container">An example of this more complex type of duplex
6513+
stream is the still-being-specified `WebSocketStream`. See its <a
6514+
href="https://github.com/ricea/websocketstream-explainer/blob/master/README.md">explainer</a> and <a
6515+
href="https://docs.google.com/document/d/1La1ehXw76HP6n1uUeks-WJGFgAnpX2tCjKts7QFJ57Y/edit#">design
6516+
notes</a>.
6517+
6518+
Because duplex streams obey the `readable`/`writable` property contract, they can be used with
6519+
{{ReadableStream/pipeThrough()}}. This doesn't always make sense, but it could in cases where the
6520+
underlying resource is in fact performing some sort of transformation.
6521+
6522+
<p class="example" id="example-duplex-pipethrough">For an arbitrary WebSocket, piping through a
6523+
WebSocket-derived duplex stream doesn't make sense. However, if the WebSocket server is specifically
6524+
written so that it responds to incoming messages by sending the same data back in some transformed
6525+
form, then this could be useful and convenient.
6526+
6527+
<h4 id="other-specs-endpoints">Endpoint pairs</h4>
6528+
6529+
Another type of readable/writable pair is a <dfn export>endpoint pair</dfn>. In these cases the
6530+
readable and writable streams represent the two ends of a longer pipeline, with the intention that
6531+
web developer code insert [=transform streams=] into the middle of them.
6532+
6533+
<div class="example" id="example-endpoint-pair-usage">
6534+
Assuming we had a web-platform-provided function `createEndpointPair()`, web developers would write
6535+
code like so:
6536+
6537+
<xmp highlight="js">
6538+
const { readable, writable } = createEndpointPair();
6539+
readable.pipeThrough(new TransformStream(...)).pipeTo(writable);
6540+
</xmp>
6541+
</div>
6542+
6543+
<p class="example" id="example-endpoint-pair-webrtc"><cite>WebRTC Insertable Media using
6544+
Streams</cite> is an example of this technique, with its `sender.createEncodedStreams()` and
6545+
`receiver.createEncodedStreams()` methods.
6546+
<!-- TODO cite it and cross-link to it https://github.com/tobie/specref/issues/620 -->
6547+
6548+
Despite such endpoint pairs obeying the `readable`/`writable` property contract, it never makes
6549+
sense to pass them to {{ReadableStream/pipeThrough()}}.
6550+
64766551
<h2 id="creating-examples">Examples of creating streams</h2>
64776552

64786553
<div class="non-normative">

0 commit comments

Comments
 (0)