forked from whatwg/streams
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.bs
4638 lines (3794 loc) · 220 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<pre class="metadata">
Title: Streams Standard
Group: WHATWG
H1: Streams
Shortname: streams
Repository: whatwg/streams
Inline Github Issues: title
Status: LS
Boilerplate: omit conformance, omit feedback-header
No Editor: true
Abstract: This specification provides APIs for creating, composing, and consuming streams of data that map efficiently
Abstract: to low-level I/O primitives.
Logo: https://resources.whatwg.org/logo-streams.svg
!Participate: <a href="https://github.com/whatwg/streams">GitHub whatwg/streams</a> (<a href="https://github.com/whatwg/streams/issues/new">new issue</a>, <a href="https://github.com/whatwg/streams/issues">open issues</a>)
!Participate: <a href="https://wiki.whatwg.org/wiki/IRC">IRC: #whatwg on Freenode</a>
!Commits: <a href="https://github.com/whatwg/streams/commits">GitHub whatwg/streams/commits</a>
!Commits: [SNAPSHOT-LINK]
!Commits: <a href="https://twitter.com/streamsstandard">@streamsstandard</a>
!Tests: <a href="https://github.com/w3c/web-platform-tests/tree/master/streams">web-platform-tests streams/</a> (<a href="https://github.com/w3c/web-platform-tests/labels/streams">ongoing work</a>)
!Demos: <a href="https://streams.spec.whatwg.org/demos/">streams.spec.whatwg.org/demos</a>
!Translation (non-normative): <a href="https://triple-underscore.github.io/Streams-ja.html" rel="alternate" title="Japanese" hreflang="ja" lang="ja">日本語</a>
Opaque Elements: emu-alg
Ignored Vars: e
</pre>
<pre class="anchors">
urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: %Uint8Array%; url: #sec-typedarray-objects; type: constructor
text: ArrayBuffer; url: #sec-arraybuffer-objects; type: interface
text: DataView; url: #sec-dataview-objects; type: interface
text: Number; url: #sec-ecmascript-language-types-number-type; type: interface
text: Uint8Array; url: #sec-typedarray-objects; type: interface
text: typed array; url: #sec-typedarray-objects; type: dfn
text: the typed array constructors table; url: #table-49; type: dfn
text: TypeError; url: #sec-native-error-types-used-in-this-standard-typeerror; type: exception
text: Invoke; url: #sec-invoke; type: abstract-op
</pre>
<style>
.note + .example, .note + .note { margin-top: 1em; }
emu-val { font-weight: bold; }
emu-alg > ol, emu-alg > ol ol ol ol { list-style-type: decimal; }
emu-alg > ol ol, emu-alg > ol ol ol ol ol { list-style-type: lower-alpha; }
emu-alg > ol ol ol, emu-alg > ol ol ol ol ol ol { list-style-type: lower-roman; }
emu-alg li { margin: 0; }
.heading .annotation {
background-color: beige;
border: 1px solid black;
border-radius: 3px;
cursor: help;
display: inline-block;
font-size: 70%;
font-weight: normal;
padding: 1px 2px;
}
</style>
<script src="https://resources.whatwg.org/file-issue.js" async></script>
<script src="https://resources.whatwg.org/commit-snapshot-shortcut-key.js" async></script>
<script src="https://resources.whatwg.org/dfn.js" defer></script>
<h2 id="intro">Introduction</h2>
<div class="non-normative">
<em>This section is non-normative.</em>
Large swathes of the web platform are built on streaming data: that is, data that is created, processed, and consumed
in an incremental fashion, without ever reading all of it into memory. The Streams Standard provides a common set of
APIs for creating and interfacing with such streaming data, embodied in <a>readable streams</a>,
<a>writable streams</a>, and <a>transform streams</a>.
These APIs have been designed to efficiently map to low-level I/O primitives, including specializations for byte streams
where appropriate. They allow easy composition of multiple streams into <a>pipe chains</a>, or can be used directly via
<a>readers</a> and <a>writers</a>. Finally, they are designed to automatically provide <a>backpressure</a> and queuing.
This standard provides the base stream primitives which other parts of the web platform can use to expose their
streaming data. For example, [[FETCH]] exposes {{Response}} bodies as {{ReadableStream}} instances. More generally, the
platform is full of streaming abstractions waiting to be expressed as streams: multimedia streams, file streams,
inter-global communication, and more benefit from being able to process data incrementally instead of buffering it all
into memory and processing it in one go. By providing the foundation for these streams to be exposed to developers, the
Streams Standard enables use cases like:
<ul>
<li> Video effects: piping a readable video stream through a transform stream that applies effects in real time.
<li> Decompression: piping a file stream through a transform stream that selectively decompresses files from a
<kbd>.tgz</kbd> archive, turning them into <{img}> elements as the user scrolls through an image gallery.
<li> Image decoding: piping an HTTP response stream through a transform stream that decodes bytes into bitmap data,
and then through another transform that translates bitmaps into PNGs. If installed inside the
{{ServiceWorkerGlobalScope/fetch}} hook of a service worker, this would allow developers to
transparently polyfill new image formats. [[SERVICE-WORKERS]]
</ul>
Web developers can also use the APIs described here to create their own streams, with the same APIs as those provided by
the platform. Other developers can then transparently compose platform-provided streams with those supplied by
libraries. In this way, the APIs described here provide unifying abstraction for all streams, encouraging an
ecosystem to grow around these shared and composable interfaces.
</div>
<h2 id="model">Model</h2>
A <dfn>chunk</dfn> is a single piece of data that is written to or read from a stream. It can be of any type; streams
can even contain chunks of different types. A chunk will often not be the most atomic unit of data for a given stream;
for example a byte stream might contain chunks consisting of 16 KiB {{Uint8Array}}s, instead of single
bytes.
<h3 id="rs-model">Readable Streams</h3>
A <dfn>readable stream</dfn> represents a source of data, from which you can read. In other words, data comes
<em>out</em> of a readable stream. Concretely, a readable stream is an instance of the {{ReadableStream}} class.
Although a readable stream can be created with arbitrary behavior, most readable streams wrap a lower-level I/O source,
called the <dfn>underlying source</dfn>. There are two types of underlying source: push sources and pull sources.
<dfn lt="push source">Push sources</dfn> push data at you, whether or not you are listening for it. They may also
provide a mechanism for pausing and resuming the flow of data. An example push source is a TCP socket, where data is
constantly being pushed from the OS level, at a rate that can be controlled by changing the TCP window size.
<dfn lt="pull source">Pull sources</dfn> require you to request data from them. The data may be available
synchronously, e.g. if it is held by the operating system's in-memory buffers, or asynchronously, e.g. if it has to be
read from disk. An example pull source is a file handle, where you seek to specific locations and read specific amounts.
Readable streams are designed to wrap both types of sources behind a single, unified interface. The implementation
details of a source are provided by an object with certain methods and properties. It is passed to the
{{ReadableStream()}} constructor.
<a>Chunks</a> are enqueued into the stream by the stream's <a>underlying source</a>. They can then be read one at a
time via the stream's public interface, in particular by using a <a>readable stream reader</a> acquired using the
stream's {{ReadableStream/getReader()}} method.
Code that reads from a readable stream using its public interface is known as a <dfn>consumer</dfn>.
Consumers also have the ability to <dfn lt="cancel a readable stream">cancel</dfn> a readable stream, using its
{{ReadableStream/cancel()}} method. This indicates that the consumer has lost interest in the stream, and will
immediately close the stream, throw away any queued <a>chunks</a>, and execute any cancellation mechanism of the
<a>underlying source</a>.
Consumers can also <dfn lt="tee a readable stream">tee</dfn> a readable stream using its {{ReadableStream/tee()}}
method. This will <a lt="locked to a reader">lock</a> the stream, making it no longer directly usable; however, it will
create two new streams, called <dfn lt="branches of a readable stream tee">branches</dfn>, which can be consumed
independently.
For streams representing bytes, an extended version of the <a>readable stream</a> is provided to handle bytes
efficiently, in particular by minimizing copies. The <a>underlying source</a> for such a readable stream is called
an <dfn>underlying byte source</dfn>. A readable stream whose underlying source is an underlying byte source is
sometimes called a <dfn>readable byte stream</dfn>. Consumers of a readable byte stream can acquire a <a>BYOB reader</a>
using the stream's {{ReadableStream/getReader()}} method.
<h3 id="ws-model">Writable Streams</h3>
A <dfn>writable stream</dfn> represents a destination for data, into which you can write. In other words, data goes
<em>in</em> to a writable stream. Concretely, a writable stream is an instance of the {{WritableStream}} class.
Analogously to readable streams, most writable streams wrap a lower-level I/O sink, called the
<dfn>underlying sink</dfn>. Writable streams work to abstract away some of the complexity of the underlying sink, by
queuing subsequent writes and only delivering them to the underlying sink one by one.
<a>Chunks</a> are written to the stream via its public interface, and are passed one at a time to the stream's
<a>underlying sink</a>. The implementation details of the sink are provided by an object with certain methods that is
passed to the {{WritableStream()}} constructor.
Code that writes into a writable stream using its public interface is known as a <dfn>producer</dfn>.
Producers also have the ability to <dfn lt="abort a writable stream">abort</dfn> a writable stream, using its
{{WritableStream/abort()}} method. This indicates that the producer believes something has gone wrong, and that future
writes should be discontinued. It puts the stream in an errored state, even without a signal from the <a>underlying
sink</a>.
<h3 id="ts-model">Transform Streams</h3>
<p class="warning">Transform streams are not yet fully developed. We are iterating on their design using a JavaScript
reference implementation and test suite; you can follow that work <a
href="https://github.com/whatwg/streams/labels/transform%20streams">on the issue tracker</a>. Until then, this section
gives a brief overview of their intended design, even though the details of their API is not yet determined.</p>
A <dfn>transform stream</dfn> consists of a pair of streams: a writable stream, and a readable stream.
In a manner specific to the transform stream in question, writes to the writable side result in new data being made
available for reading from the readable side. Concretely, any object with a <code>writable</code> property and a
<code>readable</code> property can serve as a transform stream. However, we plan to provide a
<code>TransformStream</code> class which makes it much easier to create such a pair that is properly entangled.
Some examples of transform streams include:
<ul>
<li>A GZIP compressor, to which uncompressed bytes are written and from which compressed bytes are read;</li>
<li>A video decoder, to which encoded bytes are written and from which uncompressed video frames are read;</li>
<li>A text decoder, to which bytes are written and from which strings are read;</li>
<li>A CSV-to-JSON converter, to which strings representing lines of a CSV file are written and from which
corresponding JavaScript objects are read.
</ul>
<h3 id="pipe-chains">Pipe Chains and Backpressure</h3>
Streams are primarily used by <dfn>piping</dfn> them to each other. A readable stream can be piped directly to a
writable stream, using its {{ReadableStream/pipeTo()}} method, or it can be piped through one or more transform streams
first, using its {{ReadableStream/pipeThrough()}} method.
A set of streams piped together in this way is referred to as a <dfn>pipe chain</dfn>. In a pipe chain, the
<dfn>original source</dfn> is the <a>underlying source</a> of the first readable stream in the chain; the
<dfn>ultimate sink</dfn> is the <a>underlying sink</a> of the final writable stream in the chain.
Once a pipe chain is constructed, it will propagate signals regarding how fast <a>chunks</a> should flow through it. If
any step in the chain cannot yet accept chunks, it propagates a signal backwards through the pipe chain, until
eventually the original source is told to stop producing chunks so fast. This process of normalizing flow from the
original source according to how fast the chain can process chunks is called <dfn>backpressure</dfn>.
When <a lt="tee a readable stream">teeing</a> a readable stream, the <a>backpressure</a> signals from its two
<a lt="branches of a readable stream tee">branches</a> will aggregate, such that if neither branch is read from, a
backpressure signal will be sent to the <a>underlying source</a> of the original stream.
Piping <a>locks</a> the readable and writable streams, preventing them from being manipulated for the duration of the
pipe operation. This allows the implementation to perform important optimizations, such as directly shuttling data from
the underlying source to the underlying sink while bypassing many of the intermediate queues.
<h3 id="queuing-strategies">Internal Queues and Queuing Strategies</h3>
Both readable and writable streams maintain <dfn>internal queues</dfn>, which they use for similar purposes. In the
case of a readable stream, the internal queue contains <a>chunks</a> that have been enqueued by the <a>underlying
source</a>, but not yet read by the consumer. In the case of a writable stream, the internal queue contains
<a>chunks</a> which have been written to the stream by the producer, but not yet processed and acknowledged by the
<a>underlying sink</a>.
A <dfn>queuing strategy</dfn> is an object that determines how a stream should signal <a>backpressure</a> based on
the state of its <a>internal queue</a>. The queuing strategy assigns a size to each <a>chunk</a>, and compares the
total size of all chunks in the queue to a specified number, known as the <dfn>high water mark</dfn>. The resulting
difference, high water mark minus total size, is used to determine the
<dfn lt="desired size to fill a stream's internal queue">desired size to fill the stream's queue</dfn>.
For readable streams, an underlying source can use this desired size as a backpressure signal, slowing down chunk
generation so as to try to keep the desired size above or at zero. For writable streams, a producer can behave
similarly, avoiding writes that would cause the desired size to go negative.
Concretely, a queueing strategy is given by any JavaScript object with a <code>highWaterMark</code> property and a
<code>size()</code> method. <!-- TODO: https://github.com/whatwg/streams/issues/427 -->
<div class="example" id="example-simple-queuing-strategy">
A simple example of a queuing strategy would be one that assigns a size of one to each chunk, and has a high water
mark of three. This would mean that up to three chunks could be enqueued in a readable stream, or three chunks
written to a writable stream, before the streams are considered to be applying backpressure.
In JavaScript, such a strategy could be written manually as <code>{ highWaterMark: 3, size() { return 1; }}</code>,
or using the built-in {{CountQueuingStrategy}} class, as <code>new CountQueuingStrategy({ highWaterMark: 3 })</code>.
</div>
<h3 id="locking">Locking</h3>
A <dfn lt="reader|readable stream reader">readable stream reader</dfn>, or simply reader, is an object that allows
direct reading of <a>chunks</a> from a <a>readable stream</a>. Without a reader, a <a>consumer</a> can only perform
high-level operations on the readable stream: <a lt="cancel a readable stream">canceling</a> the stream, or
<a>piping</a> the readable stream to a writable stream. A reader is acquired via the stream's
{{ReadableStream/getReader()}} method.
A <a>readable byte stream</a> has the ability to vend two types of readers: <dfn>default readers</dfn> and <dfn>BYOB
readers</dfn>. BYOB ("bring your own buffer") readers allow reading into a developer-supplied buffer, thus minimizing
copies. A non-byte readable stream can only vend default readers. Default readers are instances of the
{{ReadableStreamDefaultReader}} class, while BYOB readers are instances of {{ReadableStreamBYOBReader}}.
Similarly, a <dfn lt="writer|writable stream writer">writable stream writer</dfn>, or simply writer, is an object that
allows direct writing of <a>chunks</a> to a <a>writable stream</a>. Without a writer, a <a>producer</a> can only perform
the high-level operations of <a lt="abort a writable stream">aborting</a> the stream or <a>piping</a> a readable stream
to the writable stream. Writers are represented by the {{WritableStreamDefaultWriter}} class.
<p class="note">Under the covers, these high-level operations actually use a reader or writer themselves.</p>
A given readable or writable stream only has at most one reader or writer at a time. We say in this case the stream is
<dfn lt="lock|locked to a reader|locked to a writer">locked</dfn>, and that the reader or writer is <dfn
lt="active|active reader|active writer">active</dfn>. This state can be determined using the
{{ReadableStream/locked|readableStream.locked}} or {{WritableStream/locked|writableStream.locked}} properties.
A reader or writer also has the capability to <dfn lt="release a lock|release a read lock|release a write lock">release
its lock</dfn>, which makes it no longer active, and allows further readers or writers to be acquired. This is done via
the {{ReadableStreamDefaultReader/releaseLock()|defaultReader.releaseLock()}},
{{ReadableStreamBYOBReader/releaseLock()|byobReader.releaseLock()}}, or
{{WritableStreamDefaultWriter/releaseLock()|writer.releaseLock()}} method, as appropriate.
<h2 id="rs">Readable Streams</h2>
<h3 id="rs-intro">Using Readable Streams</h3>
<div class="example" id="example-basic-pipe-to">
The simplest way to consume a readable stream is to simply <a lt="piping">pipe</a> it to a <a>writable stream</a>.
This ensures that <a>backpressure</a> is respected, and any errors (either writing or reading) are propagated through
the chain:
<pre><code class="lang-javascript">
readableStream.pipeTo(writableStream)
.then(() => console.log("All data successfully written!"))
.catch(e => console.error("Something went wrong!", e));
</code></pre>
</div>
<div class="example" id="example-pipe-as-chunks-receiver">
If you simply want to be alerted of each new chunk from a readable stream, you can <a lt="piping">pipe</a> it to a
new <a>writable stream</a> that you custom-create for that purpose:
<pre><code class="lang-javascript">
readableStream.pipeTo(new WritableStream({
write(chunk) {
console.log("Chunk received", chunk);
},
close() {
console.log("All data successfully read!");
},
abort(e) {
console.error("Something went wrong!", e);
}
}));
</code></pre>
By returning promises from your <code>write</code> implementation, you can signal <a>backpressure</a> to the readable
stream.
</div>
<div class="example" id="example-manual-read">
Although readable streams will usually be used by piping them to a writable stream, you can also read them directly by
acquiring a <a>reader</a> and using its <code>read()</code> method to get successive chunks. For example, this code
logs the next <a>chunk</a> in the stream, if available:
<pre><code class="lang-javascript">
const reader = readableStream.getReader();
reader.read().then(
({ value, done }) => {
if (done) {
console.log("The stream was already closed!");
} else {
console.log(value);
}
},
e => console.error("The stream became errored and cannot be read from!", e)
);
</code></pre>
This more manual method of reading a stream is mainly useful for library authors building new high-level operations
on streams, beyond the provided ones of <a>piping</a> and <a lt="tee a readable stream">teeing</a>.
</div>
<div class="example" id="example-manual-read-bytes">
The above example showed using the readable stream's <a>default reader</a>. If the stream is a <a>readable byte
stream</a>, you can also acquire a <a>BYOB reader</a> for it, which allows more precise control over buffer
allocation in order to avoid copies. For example, this code reads the first 1024 bytes from the stream into a single
memory buffer:
<pre><code class="lang-javascript">
const reader = readableStream.getReader({ mode: "byob" });
let startingAB = new ArrayBuffer(1024);
readInto(startingAB)
.then(buffer => console.log("The first 1024 bytes:", buffer))
.catch(e => console.error("Something went wrong!", e));
function readInto(buffer, offset = 0) {
if (offset === buffer.byteLength) {
return Promise.resolve(buffer);
}
const view = new Uint8Array(buffer, offset, buffer.byteLength - offset);
return reader.read(view).then(newView => {
return readInto(newView.buffer, offset + newView.byteLength);
});
}
</code></pre>
An important thing to note here is that the final <code>buffer</code> value is different from the
<code>startingAB</code>, but it (and all intermediate buffers) shares the same backing memory allocation. At each
step, the buffer is <a href="#transfer-array-buffer">transferred</a> to a new {{ArrayBuffer}} object. The
<code>newView</code> is a new {{Uint8Array}}, with that {{ArrayBuffer}} object as its <code>buffer</code> property,
the offset that bytes were written to as its <code>byteOffset</code> property, and the number of bytes that were
written as its <code>byteLength</code> property.
</div>
<h3 id="rs-class" interface lt="ReadableStream">Class <code>ReadableStream</code></h3>
The {{ReadableStream}} class is a concrete instance of the general <a>readable stream</a> concept. It is
adaptable to any <a>chunk</a> type, and maintains an internal queue to keep track of data supplied by the <a>underlying
source</a> but not yet read by any consumer.
<h4 id="rs-class-definition">Class Definition</h4>
<em>This section is non-normative.</em>
If one were to write the {{ReadableStream}} class in something close to the syntax of [[!ECMASCRIPT]], it would look
like
<pre><code class="lang-javascript">
class ReadableStream {
constructor(underlyingSource = {}, { size, highWaterMark } = {})
get locked()
cancel(reason)
getReader()
pipeThrough({ writable, readable }, options)
pipeTo(dest, { preventClose, preventAbort, preventCancel } = {})
tee()
}
</code></pre>
<h4 id="rs-internal-slots">Internal Slots</h4>
Instances of {{ReadableStream}} are created with the internal slots described in the following table:
<table>
<thead>
<tr>
<th>Internal Slot</th>
<th>Description (<em>non-normative</em>)</th>
</tr>
</thead>
<tr>
<td>\[[disturbed]]
<td class="non-normative">A boolean flag set to <emu-val>true</emu-val> when the stream has been read from or
canceled
</tr>
<tr>
<td>\[[readableStreamController]]
<td class="non-normative">A {{ReadableStreamDefaultController}} or {{ReadableByteStreamController}} created with
the ability to control the state and queue of this stream; also used for the <a
href="#is-readable-stream">IsReadableStream</a> brand check
</tr>
<tr>
<td>\[[reader]]
<td class="non-normative">A {{ReadableStreamDefaultReader}} or {{ReadableStreamBYOBReader}} instance, if the stream
is <a>locked to a reader</a>, or <emu-val>undefined</emu-val> if it is not
</tr>
<tr>
<td>\[[state]]
<td class="non-normative">A string containing the stream's current state, used internally; one of
<code>"readable"</code>, <code>"closed"</code>, or <code>"errored"</code>
</tr>
<tr>
<td>\[[storedError]]
<td class="non-normative">A value indicating how the stream failed, to be given as a failure reason or exception
when trying to operate on an errored stream
</tr>
</table>
<h4 id="rs-constructor" constructor for="ReadableStream" lt="ReadableStream(underlyingSource, queuingStrategy)">new
ReadableStream(<var>underlyingSource</var> = {}, { <var>size</var>, <var>highWaterMark</var> } = {})</h4>
<div class="note">
The <code>underlyingSource</code> object passed to the constructor can implement any of the following methods to
govern how the constructed stream instance behaves:
<ul>
<li> <code>start(controller)</code> is called immediately, and is typically used to adapt a <a>push
source</a> by setting up relevant event listeners, or to acquire access to a <a>pull source</a>. If this process
is asynchronous, it can return a promise to signal success or failure.
<li> <code>pull(controller)</code> is called when the stream's <a>internal queue</a> of chunks is not full, and
will be called repeatedly until the queue reaches its <a>high water mark</a>. If <code>pull</code> returns a
promise, then <code>pull</code> will not be called again until that promise fulfills; if the promise rejects, the
stream will become errored.
<li> <code>cancel(reason)</code> is called when the consumer signals that they are no longer interested in the
stream. It can perform any actions necessary to release access to the <a>underlying source</a>. If this
process is asynchronous, it can return a promise to signal success or failure.
</ul>
Both <code>start</code> and <code>pull</code> are given the ability to manipulate the stream's internal queue and
state via the passed <code>controller</code> object. This is an example of the
<a href="https://blog.domenic.me/the-revealing-constructor-pattern/">revealing constructor pattern</a>.
If the <code>underlyingSource</code> object contains a property <code>type</code> set to <code>"bytes"</code>, this
<a>readable stream</a> is a <a>readable byte stream</a>, and can successfully vend <a>BYOB readers</a>. In that case,
the passed <code>controller</code> object will be an instance of {{ReadableByteStreamController}}. Otherwise, it will
be an instance of {{ReadableStreamDefaultController}}.
For <a>readable byte streams</a>, <code>underlyingSource</code> can also contain a property
<code>autoAllocateChunkSize</code>, which can be set to a positive integer to enable the auto-allocation feature for
this stream. In that case, when a <a>consumer</a> uses a <a>default reader</a>, the stream implementation will
automatically allocate an {{ArrayBuffer}} of the given size, and call the <a>underlying source</a> code
as if the <a>consumer</a> was using a <a>BYOB reader</a>. This can cut down on the amount of code needed when writing
the <a>underlying source</a> implementation, as can be seen by comparing [[#example-rbs-push]] without auto-allocation
to [[#example-rbs-pull]] with auto-allocation.
The constructor also accepts a second argument containing the <a>queuing strategy</a> object with
two properties: a non-negative number <code>highWaterMark</code>, and a function <code>size(chunk)</code>. The
supplied <code>strategy</code> could be an instance of the built-in {{CountQueuingStrategy}} or
{{ByteLengthQueuingStrategy}} classes, or it could be custom. If no strategy is supplied, the default
behavior will be the same as a {{CountQueuingStrategy}} with a <a>high water mark</a> of 1.
</div>
<emu-alg>
1. Set *this*.[[state]] to `"readable"`.
1. Set *this*.[[reader]] and *this*.[[storedError]] to *undefined*.
1. Set *this*.[[disturbed]] to *false*.
1. Set *this*.[[readableStreamController]] to *undefined*.
1. Let _type_ be ? GetV(_underlyingSource_, `"type"`).
1. Let _typeString_ be ? ToString(_type_).
1. If _typeString_ is `"bytes"`,
1. If _highWaterMark_ is *undefined*, let _highWaterMark_ be *0*.
1. Set *this*.[[readableStreamController]] to ? Construct(`<a idl>ReadableByteStreamController</a>`, « *this*,
_underlyingSource_, _highWaterMark_ »).
1. Otherwise, if _type_ is *undefined*,
1. If _highWaterMark_ is *undefined*, let _highWaterMark_ be *1*.
1. Set *this*.[[readableStreamController]] to ? Construct(`<a idl>ReadableStreamDefaultController</a>`, « *this*,
_underlyingSource_, _size_, _highWaterMark_ »).
1. Otherwise, throw a *RangeError* exception.
</emu-alg>
<h4 id="rs-prototype">Properties of the {{ReadableStream}} Prototype</h4>
<h5 id="rs-locked" attribute for="ReadableStream" lt="locked">get locked</h5>
<div class="note">
The <code>locked</code> getter returns whether or not the readable stream is <a>locked to a reader</a>.
</div>
<emu-alg>
1. If ! IsReadableStream(*this*) is *false*, throw a *TypeError* exception.
1. Return ! IsReadableStreamLocked(*this*).
</emu-alg>
<h5 id="rs-cancel" method for="ReadableStream">cancel(<var>reason</var>)</h5>
<div class="note">
The <code>cancel</code> method <a lt="cancel a readable stream">cancels</a> the stream, signaling a loss of interest
in the stream by a consumer. The supplied <code>reason</code> argument will be given to the underlying source, which
might or might not use it.
</div>
<emu-alg>
1. If ! IsReadableStream(*this*) is *false*, return <a>a promise rejected with</a> a *TypeError* exception.
1. If ! IsReadableStreamLocked(*this*) is *true*, return <a>a promise rejected with</a> a *TypeError* exception.
1. Return ! ReadableStreamCancel(*this*, _reason_).
</emu-alg>
<h5 id="rs-get-reader" method for="ReadableStream">getReader({ <var>mode</var> } = {})</h5>
<div class="note">
The <code>getReader</code> method creates a reader of the type specified by the <code>mode</code> option and <a
lt="locked to a reader">locks</a> the stream to the new reader. While the stream is locked, no other reader can be
acquired until this one is <a lt="release a read lock">released</a>.
This functionality is especially useful for creating abstractions that desire the ability to consume a stream in its
entirety. By getting a reader for the stream, you can ensure nobody else can interleave reads with yours or cancel
the stream, which would interfere with your abstraction.
When <code>mode</code> is <emu-val>undefined</emu-val>, the method creates a <a>default reader</a> (an instance of
{{ReadableStreamDefaultReader}}). The reader provides the ability to directly read individual <a>chunks</a> from the
stream via the reader's {{ReadableStreamDefaultReader/read()}} method.
When <code>mode</code> is <code>"byob"</code>, the <code>getReader</code> method creates a <a>BYOB reader</a> (an
instance of {{ReadableStreamBYOBReader}}). This feature only works on <a>readable byte streams</a>, i.e. streams which
were constructed specifically with the ability to handle "bring your own buffer" reading. The reader provides the
ability to directly read individual <a>chunks</a> from the stream via the reader's {{ReadableStreamBYOBReader/read()}}
method, into developer-supplied buffers, allowing more precise control over allocation.
</div>
<emu-alg>
1. If ! IsReadableStream(*this*) is *false*, throw a *TypeError* exception.
1. If _mode_ is *undefined*, return ? AcquireReadableStreamDefaultReader(*this*).
1. Set _mode_ to ? ToString(_mode_).
1. If _mode_ is `"byob"`, return ? AcquireReadableStreamBYOBReader(*this*).
1. Throw a *RangeError* exception.
</emu-alg>
<div class="example" id="example-read-all-chunks">
An example of an abstraction that might benefit from using a reader is a function like the following, which is
designed to read an entire readable stream into memory as an array of <a>chunks</a>.
<pre><code class="lang-javascript">
function readAllChunks(readableStream) {
const reader = readableStream.getReader();
const chunks = [];
return pump();
function pump() {
return reader.read().then(({ value, done }) => {
if (done) {
return chunks;
}
chunks.push(value);
return pump();
});
}
}
</code></pre>
Note how the first thing it does is obtain a reader, and from then on it uses the reader exclusively. This ensures
that no other consumer can interfere with the stream, either by reading chunks or by
<a lt="cancel a readable stream">canceling</a> the stream.
</div>
<h5 id="rs-pipe-through" method for="ReadableStream" lt="pipeThrough(transform, options)">pipeThrough({
<var>writable</var>, <var>readable</var> }, <var>options</var>)</h5>
<div class="note">
The <code>pipeThrough</code> method provides a convenient, chainable way of <a>piping</a> this <a>readable stream</a>
through a <a>transform stream</a> (or any other <code>{ writable, readable }</code> pair). It simply pipes the stream
into the writable side of the supplied pair, and returns the readable side for further use.
Piping a stream will generally <a lt="locked to a reader">lock</a> it for the duration of the pipe, preventing any
other consumer from acquiring a reader.
This method is intentionally generic; it does not require that its <emu-val>this</emu-val> value be a
{{ReadableStream}} object. It also does not require that its <code>writable</code> argument be a {{WritableStream}}
instance, or that its <code>readable</code> argument be a {{ReadableStream}} instance.
</div>
<emu-alg>
1. If _writable_ is *undefined* or _readable_ is *undefined*, throw a *TypeError* exception indicating that undefined
values are not permitted.
<p class="note"><emu-val>undefined</emu-val> is special-cased here to make it easier to debug errors such as
<code>rs.pipeThrough(writable, readable)</code> which otherwise would silently do nothing. <code><a
lt="pipeThrough()" for="ReadableStream">pipeThrough()</a></code> is
otherwise agnostic about the values that it passes through.</p>
1. Let _promise_ be ? Invoke(*this*, `"pipeTo"`, « _writable_, _options_ »).
1. If Type(_promise_) is Object and _promise_ has a [[PromiseIsHandled]] internal slot, set
_promise_.[[PromiseIsHandled]] to *true*.
1. Return _readable_.
</emu-alg>
<div class="example" id="example-pipe-chain">
A typical example of constructing <a>pipe chain</a> using {{ReadableStream/pipeThrough(transform, options)}} would
look like
<pre><code class="lang-javascript">
httpResponseBody
.pipeThrough(decompressorTransform)
.pipeThrough(ignoreNonImageFilesTransform)
.pipeTo(mediaGallery);
</code></pre>
</div>
<h5 id="rs-pipe-to" method for="ReadableStream" lt="pipeTo(dest, options)">pipeTo(<var>dest</var>, {
<var>preventClose</var>, <var>preventAbort</var>, <var>preventCancel</var> } = {})</h5>
<div class="note">
The <code>pipeTo</code> method <a lt="piping">pipes</a> this <a>readable stream</a> to a given <a>writable
stream</a>. The way in which the piping process behaves under various error conditions can be customized with a
number of passed options. It returns a promise that fulfills when the piping process completes successfully, or
rejects if any errors were encountered.
Piping a stream will <a lt="locked to a reader">lock</a> it for the duration of the pipe, preventing any other
consumer from acquiring a reader.
Errors and closures of the source and destination streams propagate as follows:
<ul>
<li><p>An error in the source <a>readable stream</a> will <a lt="abort a writable stream">abort</a> the destination
<a>writable stream</a>, unless <code>preventAbort</code> is truthy. The returned promise will be rejected with the
source's error, or with any error that occurs during aborting the destination.</p></li>
<li><p>An error in the destination <a>writable stream</a> will <a lt="cancel a readable stream">cancel</a> the
source <a>readable stream</a>, unless <code>preventCancel</code> is truthy. The returned promise will be rejected
with the destination's error, or with any error that occurs during canceling the source.</p></li>
<li><p>When the source <a>readable stream</a> closes, the destination <a>writable stream</a> will be closed, unless
<code>preventClose</code> is true. The returned promise will be fulfilled once this process completes, unless an
error is encountered while closing the destination, in which case it will be rejected with that error.</p></li>
<li><p>If the destination <a>writable stream</a> starts out closed or closing, the source <a>readable stream</a>
will be <a lt="cancel a readable stream">canceled</a>, unless <code>preventCancel</code> is true. The returned
promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs
during canceling the source.</p></li>
</ul>
</div>
<emu-alg>
1. If ! IsReadableStream(*this*) is *false*, return <a>a promise rejected with</a> a *TypeError* exception.
1. If ! IsWritableStream(_dest_) is *false*, return <a>a promise rejected with</a> a *TypeError* exception.
1. Set _preventClose_ to ! ToBoolean(_preventClose_), set _preventAbort_ to ! ToBoolean(_preventAbort_), and set
_preventCancel_ to ! ToBoolean(_preventCancel_).
1. If ! IsReadableStreamLocked(*this*) is *true*, return <a>a promise rejected with</a> a *TypeError* exception.
1. If ! IsWritableStreamLocked(_dest_) is *true*, return <a>a promise rejected with</a> a *TypeError* exception.
1. If ! IsReadableByteStreamController(*this*.[[readableStreamController]]) is *true*, let _reader_ be either !
AcquireReadableStreamBYOBReader(*this*) or ! AcquireReadableStreamDefaultReader(*this*), at the user agent's
discretion.
1. Otherwise, let _reader_ be ! AcquireReadableStreamDefaultReader(*this*).
1. Let _writer_ be ! AcquireWritableStreamDefaultWriter(_dest_).
1. Let _shuttingDown_ be *false*.
1. Let _promise_ be <a>a new promise</a>.
1. <a>In parallel</a>, using _reader_ and _writer_, read all <a>chunks</a> from *this* and write them to _dest_. Due
to the locking provided by the reader and writer, the exact manner in which this happens is not observable to
author code, and so there is flexibility in how this is done. The following constraints apply regardless of the
exact algorithm used:
* <strong>Public API must not be used:</strong> while reading or writing, or performing any of the operations
below, the JavaScript-modifiable reader, writer, and stream APIs (i.e. methods on the appropriate prototypes)
must not be used. Instead, the streams must be manipulated directly.
* <strong>Backpressure must be enforced:</strong>
* While WritableStreamDefaultWriterGetDesiredSize(_writer_) is ≤ *0* or is *null*, the user agent must not read
from _reader_.
* If _reader_ is a <a>BYOB reader</a>, WritableStreamDefaultWriterGetDesiredSize(_writer_) should be used to
determine the size of the chunks read from _reader_.
* Reads or writes should not be delayed for reasons other than these backpressure signals.
<p class="example" id="example-bad-backpressure">An implementation that waits for each write to successfully
complete before proceeding to the next read/write operation violates this recommendation. In doing so, such an
implementation makes the <a>internal queue</a> of _dest_ useless, as it ensures _dest_ always contains at most
one queued <a>chunk</a>.</p>
* <strong>Shutdown must stop activity:</strong> if _shuttingDown_ becomes *true*, the user agent must not
initiate further reads from _reader_, and must only perform writes of already-read <a>chunks</a>, as described
below. In particular, the user agent must check the below conditions before performing any reads or writes,
since they might lead to immediate shutdown.
* <strong>Error and close states must be propagated:</strong> the following conditions must be applied in order.
1. <strong>Errors must be propagated forward:</strong> if *this*.[[state]] is or becomes `"errored"`, then
1. If _preventAbort_ is *false*, <a href="#rs-pipeTo-shutdown-with-action">shutdown with an action</a> of !
WritableStreamAbort(_dest_, *this*.[[storedError]]) and with *this*.[[storedError]].
1. Otherwise, <a href="#rs-pipeTo-shutdown">shutdown</a> with *this*.[[storedError]].
1. <strong>Errors must be propagated backward:</strong> if _dest_.[[state]] is or becomes `"errored"`, then
1. If _preventCancel_ is *false*, <a href="#rs-pipeTo-shutdown-with-action">shutdown with an action</a> of !
ReadableStreamCancel(*this*, _dest_.[[storedError]]) and with _dest_.[[storedError]].
1. Otherwise, <a href="#rs-pipeTo-shutdown">shutdown</a> with _dest_.[[storedError]].
1. <strong>Closing must be propagated forward:</strong> if *this*.[[state]] is or becomes `"closed"`, then
1. If _preventClose_ is *false*, <a href="#rs-pipeTo-shutdown-with-action">shutdown with an action</a> of !
WritableStreamDefaultWriterCloseWithErrorPropagation(_writer_).
1. Otherwise, <a href="#rs-pipeTo-shutdown">shutdown</a>.
1. <strong>Closing must be propagated backward:</strong> if ! WritableStreamCloseQueuedOrInFlight(_dest_) is *true*
or _dest_.[[state]] is `"closed"`, then
1. Assert: no <a>chunks</a> have been read or written.
1. Let _destClosed_ be a new *TypeError*.
1. If _preventCancel_ is *false*, <a href="#rs-pipeTo-shutdown-with-action">shutdown with an action</a> of !
ReadableStreamCancel(*this*, _destClosed_) and with _destClosed_.
1. Otherwise, <a href="#rs-pipeTo-shutdown">shutdown</a> with _destClosed_.
* <i id="rs-pipeTo-shutdown-with-action">Shutdown with an action</i>: if any of the above requirements ask to
shutdown with an action _action_, optionally with an error _originalError_, then:
1. If _dest_.[[state]] is `"writable"` and ! WritableStreamCloseQueuedOrInFlight(dest) is *false*,
1. If any <a>chunks</a> have been read but not yet written, write them to _dest_.
1. Wait until every <a>chunk</a> that has been read has been written (i.e. the corresponding promises have
settled).
1. If _shuttingDown_ is *true*, abort these substeps.
1. Set _shuttingDown_ to *true*.
1. Let _p_ be the result of performing _action_.
1. <a>Upon fulfillment</a> of _p_, <a href="#rs-pipeTo-finalize">finalize</a>, passing along _originalError_ if
it was given.
1. <a>Upon rejection</a> of _p_ with reason _newError_, <a href="#rs-pipeTo-finalize">finalize</a> with
_newError_.
* <i id="rs-pipeTo-shutdown">Shutdown</i>: if any of the above requirements or steps ask to shutdown, optionally
with an error _error_, then:
1. If _dest_.[[state]] is `"writable"` and ! WritableStreamCloseQueuedOrInFlight(dest) is *false*,
1. If any <a>chunks</a> have been read but not yet written, write them to _dest_.
1. Wait until every <a>chunk</a> that has been read has been written (i.e. the corresponding promises have
settled).
1. If _shuttingDown_ is *true*, abort these substeps.
1. Set _shuttingDown_ to *true*.
1. <a href="#rs-pipeTo-finalize">Finalize</a>, passing along _error_ if it was given.
* <i id="rs-pipeTo-finalize">Finalize</i>: both forms of shutdown will eventually ask to finalize, optionally with
an error _error_, which means to perform the following steps:
1. Perform ! WritableStreamDefaultWriterRelease(_writer_).
1. Perform ! ReadableStreamReaderGenericRelease(_reader_).
1. If _error_ was given, <a>reject</a> _promise_ with _error_.
1. Otherwise, <a>resolve</a> _promise_ with *undefined*.
1. Return _promise_.
</emu-alg>
<h5 id="rs-tee" method for="ReadableStream">tee()</h5>
<div class="note">
The <code>tee</code> method <a lt="tee a readable stream">tees</a> this readable stream, returning a two-element
array containing the two resulting branches as new {{ReadableStream}} instances.
Teeing a stream will <a lt="locked to a reader">lock</a> it, preventing any other consumer from acquiring a reader.
To <a lt="cancel a readable stream">cancel</a> the stream, cancel both of the resulting branches; a composite
cancellation reason will then be propagated to the stream's <a>underlying source</a>.
Note that the <a>chunks</a> seen in each branch will be the same object. If the chunks are not immutable, this could
allow interference between the two branches.
</div>
<emu-alg>
1. If ! IsReadableStream(*this*) is *false*, throw a *TypeError* exception.
1. Let _branches_ be ? ReadableStreamTee(*this*, *false*).
1. Return ! CreateArrayFromList(_branches_).
</emu-alg>
<div class="example" id="example-tee-and-pipe">
Teeing a stream is most useful when you wish to let two independent consumers read from the stream in parallel,
perhaps even at different speeds. For example, given a writable stream <code>cacheEntry</code> representing an
on-disk file, and another writable stream <code>httpRequestBody</code> representing an upload to a remote server,
you could pipe the same readable stream to both destinations at once:
<pre><code class="lang-javascript">
const [forLocal, forRemote] = readableStream.tee();
Promise.all([
forLocal.pipeTo(cacheEntry),
forRemote.pipeTo(httpRequestBody)
])
.then(() => console.log("Saved the stream to the cache and also uploaded it!"))
.catch(e => console.error("Either caching or uploading failed: ", e));
</code></pre>
</div>
<h3 id="rs-abstract-ops">General Readable Stream Abstract Operations</h3>
The following abstract operations, unlike most in this specification, are meant to be generally useful by other
specifications, instead of just being part of the implementation of this spec's classes.
<h4 id="acquire-readable-stream-byob-reader" aoid="AcquireReadableStreamBYOBReader"
throws>AcquireReadableStreamBYOBReader ( <var>stream</var> )</h4>
This abstract operation is meant to be called from other specifications that may wish to acquire a <a>BYOB reader</a>
for a given stream.
<emu-alg>
1. Return ? Construct(`<a idl>ReadableStreamBYOBReader</a>`, « _stream_ »).
</emu-alg>
<h4 id="acquire-readable-stream-reader" aoid="AcquireReadableStreamDefaultReader" throws
export>AcquireReadableStreamDefaultReader ( <var>stream</var> )</h4>
This abstract operation is meant to be called from other specifications that may wish to acquire a <a>default
reader</a> for a given stream.
<emu-alg>
1. Return ? Construct(`<a idl>ReadableStreamDefaultReader</a>`, « _stream_ »).
</emu-alg>
<h4 id="is-readable-stream" aoid="IsReadableStream" nothrow>IsReadableStream ( <var>x</var> )</h4>
<emu-alg>
1. If Type(_x_) is not Object, return *false*.
1. If _x_ does not have a [[readableStreamController]] internal slot, return *false*.
1. Return *true*.
</emu-alg>
<h4 id="is-readable-stream-disturbed" aoid="IsReadableStreamDisturbed" nothrow export>IsReadableStreamDisturbed (
<var>stream</var> )</h4>
This abstract operation is meant to be called from other specifications that may wish to query whether or not a
readable stream has ever been read from or canceled.
<emu-alg>
1. Assert: ! IsReadableStream(_stream_) is *true*.
1. Return _stream_.[[disturbed]].
</emu-alg>
<h4 id="is-readable-stream-locked" aoid="IsReadableStreamLocked" nothrow export>IsReadableStreamLocked (
<var>stream</var> )</h4>
This abstract operation is meant to be called from other specifications that may wish to query whether or not a
readable stream is <a>locked to a reader</a>.
<emu-alg>
1. Assert: ! IsReadableStream(_stream_) is *true*.
1. If _stream_.[[reader]] is *undefined*, return *false*.
1. Return *true*.
</emu-alg>
<h4 id="readable-stream-tee" aoid="ReadableStreamTee" throws export>ReadableStreamTee ( <var>stream</var>,
<var>cloneForBranch2</var> )</h4>
This abstract operation is meant to be called from other specifications that may wish to <a lt="tee a readable
stream">tee</a> a given readable stream.
The second argument, <var>cloneForBranch2</var>, governs whether or not the data from the original stream will be cloned
(using HTML's <a>serializable objects</a> framework) before appearing in the second of the returned branches. This is
useful for scenarios where both branches are to be consumed in such a way that they might otherwise interfere with each
other, such as by <a lt="transferable objects">transferring</a> their <a>chunks</a>. However, it does introduce a
noticable asymmetry between the two branches, and limits the possible <a>chunks</a> to serializable ones. [[!HTML]]
<p class="note">In this standard ReadableStreamTee is always called with <var>cloneForBranch2</var> set to
<emu-val>false</emu-val>; other specifications pass <emu-val>true</emu-val>.</p>
<emu-alg>
1. Assert: ! IsReadableStream(_stream_) is *true*.
1. Assert: Type(_cloneForBranch2_) is Boolean.
1. Let _reader_ be ? AcquireReadableStreamDefaultReader(_stream_).
1. Let _teeState_ be Record {[[closedOrErrored]]: *false*, [[canceled1]]: *false*, [[canceled2]]: *false*,
[[reason1]]: *undefined*, [[reason2]]: *undefined*, [[promise]]: <a>a new promise</a>}.
1. Let _pull_ be a new <a>ReadableStreamTee pull function</a>.
1. Set _pull_.[[reader]] to _reader_, _pull_.[[teeState]] to _teeState_, and _pull_.[[cloneForBranch2]] to
_cloneForBranch2_.
1. Let _cancel1_ be a new <a>ReadableStreamTee branch 1 cancel function</a>.
1. Set _cancel1_.[[stream]] to _stream_ and _cancel1_.[[teeState]] to _teeState_.
1. Let _cancel2_ be a new <a>ReadableStreamTee branch 2 cancel function</a>.
1. Set _cancel2_.[[stream]] to _stream_ and _cancel2_.[[teeState]] to _teeState_.
1. Let _underlyingSource1_ be ! ObjectCreate(%ObjectPrototype%).
1. Perform ! CreateDataProperty(_underlyingSource1_, `"pull"`, _pull_).
1. Perform ! CreateDataProperty(_underlyingSource1_, `"cancel"`, _cancel1_).
1. Let _branch1Stream_ be ! Construct(`<a idl>ReadableStream</a>`, _underlyingSource1_).
1. Let _underlyingSource2_ be ! ObjectCreate(%ObjectPrototype%).
1. Perform ! CreateDataProperty(_underlyingSource2_, `"pull"`, _pull_).
1. Perform ! CreateDataProperty(_underlyingSource2_, `"cancel"`, _cancel2_).
1. Let _branch2Stream_ be ! Construct(`<a idl>ReadableStream</a>`, _underlyingSource2_).
1. Set _pull_.[[branch1]] to _branch1Stream_.[[readableStreamController]].
1. Set _pull_.[[branch2]] to _branch2Stream_.[[readableStreamController]].
1. <a>Upon rejection</a> of _reader_.[[closedPromise]] with reason _r_,
1. If _teeState_.[[closedOrErrored]] is *false*, then:
1. Perform ! ReadableStreamDefaultControllerError(_pull_.[[branch1]], _r_).
1. Perform ! ReadableStreamDefaultControllerError(_pull_.[[branch2]], _r_).
1. Set _teeState_.[[closedOrErrored]] to *true*.
1. Return « _branch1Stream_, _branch2Stream_ ».
</emu-alg>
A <dfn>ReadableStreamTee pull function</dfn> is an anonymous built-in function that pulls data from a given <a>readable
stream reader</a> and enqueues it into two other streams ("branches" of the associated tee). Each ReadableStreamTee
pull function has \[[reader]], \[[branch1]], \[[branch2]], \[[teeState]], and \[[cloneForBranch2]] internal slots. When
a ReadableStreamTee pull function <var>F</var> is called, it performs the following steps:
<emu-alg>
1. Let _reader_ be _F_.[[reader]], _branch1_ be _F_.[[branch1]], _branch2_ be _F_.[[branch2]], _teeState_ be
_F_.[[teeState]], and _cloneForBranch2_ be _F_.[[cloneForBranch2]].
1. Return the result of <a>transforming</a> ! ReadableStreamDefaultReaderRead(_reader_) with a fulfillment handler
which takes the argument _result_ and performs the following steps:
1. Assert: Type(_result_) is Object.
1. Let _value_ be ? Get(_result_, `"value"`).
1. Let _done_ be ? Get(_result_, `"done"`).
1. Assert: Type(_done_) is Boolean.
1. If _done_ is *true* and _teeState_.[[closedOrErrored]] is *false*,
1. If _teeState_.[[canceled1]] is *false*,
1. Perform ! ReadableStreamDefaultControllerClose(_branch1_).
1. If _teeState_.[[canceled2]] is *false*,
1. Perform ! ReadableStreamDefaultControllerClose(_branch2_).
1. Set _teeState_.[[closedOrErrored]] to *true*.
1. If _teeState_.[[closedOrErrored]] is *true*, return.
1. Let _value1_ and _value2_ be _value_.
1. If _teeState_.[[canceled2]] is *false* and _cloneForBranch2_ is *true*, set _value2_ to ? <a
abstract-op>StructuredDeserialize</a>(<a abstract-op>StructuredSerialize</a>(_value2_), the current Realm
Record).
1. If _teeState_.[[canceled1]] is *false*, perform ? ReadableStreamDefaultControllerEnqueue(_branch1_, _value1_).
1. If _teeState_.[[canceled2]] is *false*, perform ? ReadableStreamDefaultControllerEnqueue(_branch2_, _value2_).
</emu-alg>
A <dfn>ReadableStreamTee branch 1 cancel function</dfn> is an anonymous built-in function that reacts to the
cancellation of the first of the two branches of the associated tee. Each ReadableStreamTee branch 1 cancel function
has \[[stream]] and \[[teeState]] internal slots. When a ReadableStreamTee branch 1 cancel function <var>F</var> is
called with argument <var>reason</var>, it performs the following steps:
<emu-alg>
1. Let _stream_ be _F_.[[stream]] and _teeState_ be _F_.[[teeState]].
1. Set _teeState_.[[canceled1]] to *true*.
1. Set _teeState_.[[reason1]] to _reason_.
1. If _teeState_.[[canceled2]] is *true*,
1. Let _compositeReason_ be ! CreateArrayFromList(« _teeState_.[[reason1]], _teeState_.[[reason2]] »).
1. Let _cancelResult_ be ! ReadableStreamCancel(_stream_, _compositeReason_).
1. <a>Resolve</a> _teeState_.[[promise]] with _cancelResult_.
1. Return _teeState_.[[promise]].
</emu-alg>
A <dfn>ReadableStreamTee branch 2 cancel function</dfn> is an anonymous built-in function that reacts to the
cancellation of the second of the two branches of the associated tee. Each ReadableStreamTee branch 2 cancel function
has \[[stream]] and \[[teeState]] internal slots. When a ReadableStreamTee branch 2 cancel function <var>F</var> is
called with argument <var>reason</var>, it performs the following steps:
<emu-alg>
1. Let _stream_ be _F_.[[stream]] and _teeState_ be _F_.[[teeState]].
1. Set _teeState_.[[canceled2]] to *true*.
1. Set _teeState_.[[reason2]] to _reason_.
1. If _teeState_.[[canceled1]] is *true*,
1. Let _compositeReason_ be ! CreateArrayFromList(« _teeState_.[[reason1]], _teeState_.[[reason2]] »).
1. Let _cancelResult_ be ! ReadableStreamCancel(_stream_, _compositeReason_).
1. <a>Resolve</a> _teeState_.[[promise]] with _cancelResult_.
1. Return _teeState_.[[promise]].
</emu-alg>
<div class="note">
The algorithm given here is written such that three new function objects are created for each call to to
ReadableStreamTee. This is just a simplification, and is not actually necessary, since it is unobservable to
developer code. For example, a self-hosted implementation could optimize by creating a class whose prototype contains
methods for these functions, with the state stored as instance variables.
</div>
<h3 id="rs-abstract-ops-used-by-controllers">The Interface Between Readable Streams and Controllers</h3>
In terms of specification factoring, the way that the {{ReadableStream}} class encapsulates the behavior of
both simple readable streams and <a>readable byte streams</a> into a single class is by centralizing most of the
potentially-varying logic inside the two controller classes, {{ReadableStreamDefaultController}} and
{{ReadableByteStreamController}}. Those classes define most of the stateful internal slots and abstract
operations for how a stream's <a>internal queue</a> is managed and how it interfaces with its <a>underlying source</a>
or <a>underlying byte source</a>.
Each controller class defines two internal methods, which are called by the {{ReadableStream}} algorithms:
<dl>
<dt><dfn abstract-op lt="[[CancelSteps]]">\[[CancelSteps]](<var>reason</var>)</dfn></dt>
<dd>The controller's steps that run in reaction to the stream being <a lt="cancel a readable stream">canceled</a>,
used to clean up the state stored in the controller and inform the <a>underlying source</a>.</dd>
<dt><dfn abstract-op lt="[[PullSteps]]">\[[PullSteps]]()</dfn></dt>
<dd>The controller's steps that run when a <a>default reader</a> is read from, used to pull from the controller any
queued <a>chunks</a>, or pull from the <a>underlying source</a> to get more chunks.</dd>
</dl>
(These are defined as internal methods, instead of as abstract operations, so that they can be called polymorphically by
the {{ReadableStream}} algorithms, without having to branch on which type of controller is present.)
The rest of this section concerns abstract operations that go in the other direction: they are used by the controller
implementations to affect their associated {{ReadableStream}} object. This translates internal state changes of the
controller into developer-facing results visible through the {{ReadableStream}}'s public API.
<h4 id="readable-stream-add-read-into-request" aoid="ReadableStreamAddReadIntoRequest"
nothrow>ReadableStreamAddReadIntoRequest ( <var>stream</var> )</h4>
<emu-alg>
1. Assert: ! IsReadableStreamBYOBReader(_stream_.[[reader]]) is *true*.
1. Assert: _stream_.[[state]] is `"readable"` or `"closed"`.
1. Let _promise_ be <a>a new promise</a>.
1. Let _readIntoRequest_ be Record {[[promise]]: _promise_}.
1. Append _readIntoRequest_ as the last element of _stream_.[[reader]].[[readIntoRequests]].
1. Return _promise_.
</emu-alg>
<h4 id="readable-stream-add-read-request" aoid="ReadableStreamAddReadRequest" nothrow>ReadableStreamAddReadRequest (
<var>stream</var> )</h4>
<emu-alg>