diff --git a/files/en-us/web/api/readablestreambyobreader/read/index.md b/files/en-us/web/api/readablestreambyobreader/read/index.md index 73e970f9f3dd279..26896edda7a6a50 100644 --- a/files/en-us/web/api/readablestreambyobreader/read/index.md +++ b/files/en-us/web/api/readablestreambyobreader/read/index.md @@ -16,7 +16,7 @@ The method takes as an argument a view on a buffer that supplied data is to be r The promise fulfills with an object that has properties `value` and `done` when data comes available, or if the stream is cancelled. If the stream is errored, the promise will be rejected with the relevant error object. -If a chunk of data is supplied, the `value` property will contain a new view. +When a chunk of data is supplied, the `value` property will contain a new view. This will be a view over the same buffer/backing memory (and of the same type) as the original `view` passed to the `read()` method, now populated with the new chunk of data. Note that once the promise fulfills, the original `view` passed to the method will be detached and no longer usable. The promise will fulfill with a `value: undefined` if the stream has been cancelled. @@ -25,16 +25,28 @@ In this case the backing memory region of `view` is discarded and not returned t The `done` property indicates whether or not more data is expected. The value is set `true` if the stream is closed or cancelled, and `false` otherwise. +The method also has an optional `options.min` argument that can be used to specify the minimum number of elements that must be available before the promise will fulfill, while the stream is active. +The view returned in the `value` property will always have at least this number of elements, except when the stream is closed. + ## Syntax ```js-nolint read(view) +read(view, options) ``` ### Parameters - `view` - : The view that data is to be read into. +- `options` {{optional_inline}} + + - : Options are as follows: + + - `min` + - : The minimum number of elements to read before the promise will fulfill while the stream is active. + If not given, the promise will resolve with at least one element, up to the maximum size of the view. + This number must not be bigger than the view that is being read into. ### Return value @@ -71,10 +83,14 @@ The following are possible: ### Exceptions - {{jsxref("TypeError")}} - - : The source object is not a `ReadableStreamBYOBReader`, the stream has no owner, the view is not an object or has become detached, the view's length is 0, or {{domxref("ReadableStreamBYOBReader.releaseLock()")}} is called (when there's is a pending read request). + - : The source object is not a `ReadableStreamBYOBReader`, the stream has no owner, the view is not an object or has become detached, the view's length is 0, `options.min` is 0, or {{domxref("ReadableStreamBYOBReader.releaseLock()")}} is called (when there's a pending read request). +- {{jsxref("RangeError")}} + - : The `options.min` value is larger than the view being written into. ## Examples +### Reading into a view + The example code here is taken from the live examples in [Using readable byte streams](/en-US/docs/Web/API/Streams_API/Using_readable_byte_streams#examples). First we create the reader using {{domxref("ReadableStream.getReader()")}} on the stream, specifying `mode: "byob"` in the options parameter. @@ -127,6 +143,290 @@ function readStream(reader) { When there is no more data in the stream, the `read()` method fulfills with an object with the property `done` set to `true`, and the function returns. +### Reading a minimum number of elements + +This example is almost exactly the same as the previous one, except that we've modified the code to read a minimum of 101 elements on each iteration. + +We've also made it into a live example. +Note that most of the code is not relevant to the example and is therefore hidden. +For more information see [Using readable byte streams](/en-US/docs/Web/API/Streams_API/Using_readable_byte_streams#examples). + + + +```js hidden +class MockHypotheticalSocket { + constructor() { + this.max_data = 800; // total amount of data to stream from "socket" + this.max_per_read = 100; // max data per read + this.min_per_read = 40; // min data per read + this.data_read = 0; // total data read so far (capped is maxdata) + this.socketData = null; + } + + // Method returning promise when this socket is readable. + select2() { + // Object used to resolve promise + const resultObj = {}; + resultObj["bytesRead"] = 0; + + return new Promise((resolve /*, reject*/) => { + if (this.data_read >= this.max_data) { + //out of data + resolve(resultObj); + return; + } + + // Emulate slow read of data + setTimeout(() => { + const numberBytesReceived = this.getNumberRandomBytesSocket(); + this.data_read += numberBytesReceived; + this.socketData = this.randomByteArray(numberBytesReceived); + resultObj["bytesRead"] = numberBytesReceived; + resolve(resultObj); + }, 500); + }); + } + + /* Read data into specified buffer offset */ + readInto(buffer, offset, length) { + let dataLength = 0; + if (this.socketData) { + dataLength = this.socketData.length; + const myView = new Uint8Array(buffer, offset, length); + // Write the length of data specified into buffer + // Code assumes buffer always bigger than incoming data + for (let i = 0; i < dataLength; i++) { + myView[i] = this.socketData[i]; + } + this.socketData = null; // Clear "socket" data after reading + } + return dataLength; + } + + // Dummy close function + close() { + return; + } + + // Return random number bytes in this call of socket + getNumberRandomBytesSocket() { + // Capped to remaining data and the max min return-per-read range + const remaining_data = this.max_data - this.data_read; + const numberBytesReceived = + remaining_data < this.min_per_read + ? remaining_data + : this.getRandomIntInclusive( + this.min_per_read, + Math.min(this.max_per_read, remaining_data), + ); + return numberBytesReceived; + } + + // Return random number between two values + getRandomIntInclusive(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1) + min); + } + + // Return random character string + randomChars(length = 8) { + let string = ""; + let choices = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()"; + + for (let i = 0; i < length; i++) { + string += choices.charAt(Math.floor(Math.random() * choices.length)); + } + return string; + } + + /* Return random Uint8Array of bytes */ + randomByteArray(bytes = 8) { + const textEncoder = new TextEncoder(); + return textEncoder.encode(this.randomChars(bytes)); + } +} +``` + + + +```css hidden +.input { + float: left; + width: 50%; +} +.output { + float: right; + width: 50%; + overflow-wrap: break-word; +} +button { + display: block; +} +``` + +```html hidden + +