Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReadableStream.from() static method #28316

Merged
merged 6 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
172 changes: 172 additions & 0 deletions files/en-us/web/api/readablestream/from_static/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
---
title: "ReadableStream: from() static method"
short-title: from()
slug: Web/API/ReadableStream/from_static
page-type: web-api-static-method
status:
- experimental
browser-compat: api.ReadableStream.from_static
---

{{APIRef("Streams")}}{{SeeCompatTable}}

The **`ReadableStream.from()`** static method returns a {{domxref("ReadableStream")}} from a provided iterable or async iterable object.

The method can be used to wrap iterable and async iterable objects as readable streams, including arrays, sets, arrays of promises, async generators, `ReadableStreams`, Node.js `readable` streams, and so on.
hamishwillee marked this conversation as resolved.
Show resolved Hide resolved

## Syntax

```js-nolint
ReadableStream.from(anyIterable)
```

### Parameters

- `anyIterable`
- : An [iterable](/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) or [async iterable](/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols) object.

### Return value

A {{domxref("ReadableStream")}}.

### Exceptions

- {{jsxref("TypeError")}}
- : Thrown if the passed parameter is not an iterable or async iterable (does not define the `@@iterator` or `@@asyncIterator` method).
Also thrown if, during iteration, the result of the next step is not an object or is a promise that does not resolve to an object.

## Examples

### Convert an async iterator to a ReadableStream
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a template structure for live samples: https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Page_structures/Code_examples#traditional_live_samples. Although technically authors have a lot of flexibility in how to organize them, I'd prefer us to use a much more consistent approach, because it might make it possible for us to extract live samples and have them be usable in other contexts.

This would include having code blocks in a consistent order, with subheadings, and with prose in a consistent place (at the start of the example)

But maybe I'm fighting a losing battle here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But maybe I'm fighting a losing battle here.

@wbamberg I did it this way, in particular, because I understood that now the code blocks include their "type" the headings are not needed or wanted. With their removal I find the documentation easier to parse, because the isn't quite so much scrolling between the relevant bits.

If you tell me I must change, then I will, but I'm not keen :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see you marked this as requiring approval, so you do need it changed. Bah. Will do.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't really tell you you must change. I can say that unless examples are consistently organized then it won't be possible to include them in other contexts, and any tool which tries to do that will most likely throw them out (or maybe we will just throw out the explanatory prose, and hope the example still makes sense).

But I don't know if this use case is important enough to be worth the cost of imposing more constraints on authors. I think it is, but maybe others don't.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the sections in for the first example, unhiding the code that shows how logging works. In the second section I just have the JavaScript section: showing it once perhaps makes sense so there is no "magic" involved in the description, but showing it twice in the page when it is not central to the example seems unhelpful.

I "fairly" strongly feel that this use case is not workable, or even all that useful, and certainly not worth the constraint. Remember all the reasons we decided to remove the page inclusion macro - same problems. The "fairly" is because perhaps you have thought through some cases where content mismatch is not an issue.

I think these headings should be removed. But only after it's agree and https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Page_structures/Code_examples is updated :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


This live example demonstrates how you can convert an async iterable to a `ReadableStream`, and then how this stream might be consumed.

#### HTML

The HTML is consists of single `<pre>` element, which is used for logging.

```html
<pre id="log"></pre>
```

#### JavaScript

The example code creates a `log()` function to write to the log HTML element.

```js
const logElement = document.getElementById("log");
function log(text) {
logElement.innerText += `${text}\n`;
}
```

It then checks if the static method is supported, and if not, logs the result.

```js
if (!ReadableStream.from) {
log("ReadableStream.from() is not supported");
}
```

The async iterable is an anonymous generator function that yields the values of 1, 2 and 3 when it is called three times.
This is passed to `ReadableStream.from()` to create the `ReadableStream`.

```js
// Define an asynchronous iterator
const asyncIterator = (async function* () {
yield 1;
yield 2;
yield 3;
})();

// Create ReadableStream from iterator
const myReadableStream = ReadableStream.from(asyncIterator);
```

[Using readable streams](/en-US/docs/Web/API/Streams_API/Using_readable_streams) demonstrates several ways to consume a stream.
The code below uses a `for ...await` loop, as this method is the simplest.
Each iteration of the loop logs the current chunk from the stream.

```js
consumeStream(myReadableStream);

// Iterate a ReadableStream asynchronously
async function consumeStream(readableStream) {
for await (const chunk of myReadableStream) {
// Do something with each chunk
// Here we just log the values
log(`chunk: ${chunk}`);
}
}
```

#### Result

The output of consuming the stream is shown below (if `ReadableStream.from()` is supported).

{{EmbedLiveSample("Convert an async iterator to a ReadableStream","100%", "80")}}

### Convert an Array to a ReadableStream

This example demonstrates how you can convert an `Array` to a `ReadableStream`.

```html hidden
<pre id="log"></pre>
```

```js hidden
const logElement = document.getElementById("log");
function log(text) {
logElement.innerText += `${text}\n`;
}

if (!ReadableStream.from) {
log("ReadableStream.from() is not supported");
}
```

#### JavaScript

The iterable is just an array of strings that is passed to `ReadableStream.from()` to create the `ReadableStream`.

```js
// An Array of vegetable names
const vegetables = ["Carrot", "Broccoli", "Tomato", "Spinach"];

// Create ReadableStream from the Array
const myReadableStream = ReadableStream.from(vegetables);
```

```js hidden
consumeStream(myReadableStream);

// Iterate a ReadableStream asynchronously
async function consumeStream(readableStream) {
for await (const chunk of myReadableStream) {
// Do something with each chunk
// Here we just log the values
log(`chunk: ${chunk}`);
}
}
```

We use the same approach as in the previous example log and to consume the stream, so that is not shown here.

#### Result

The output is shown below.

{{EmbedLiveSample("Convert an Array to a ReadableStream","100%", "100")}}

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- {{domxref("ReadableStream")}}
- [Using readable streams](/en-US/docs/Web/API/Streams_API/Using_readable_streams)
17 changes: 13 additions & 4 deletions files/en-us/web/api/readablestream/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ The `ReadableStream` interface of the [Streams API](/en-US/docs/Web/API/Streams_
- {{domxref("ReadableStream.locked")}} {{ReadOnlyInline}}
- : Returns a boolean indicating whether or not the readable stream is locked to a reader.

## Static methods

- {{domxref("ReadableStream/from_static", "ReadableStream.from()")}} {{Experimental_Inline}}
- : Returns `ReadableStream` from a provided iterable or async iterable object, such as an array, a set, an async generator, and so on.

## Instance methods

- {{domxref("ReadableStream.cancel()")}}
Expand Down Expand Up @@ -112,9 +117,15 @@ fetch("https://www.example.org")
});
```

### Convert async iterator to stream
### Convert an iterator or async iterator to a stream

Converting an [(async) iterator](/en-US/docs/Web/JavaScript/Guide/Iterators_and_generators) to a readable stream:
The {{domxref("ReadableStream/from_static", "from()")}} static method can convert an iterator, such as an {{jsxref("Array")}} or {{jsxref("Map")}}, or an [(async) iterator](/en-US/docs/Web/JavaScript/Guide/Iterators_and_generators) to a readable stream:

```js
const myReadableStream = ReadableStream.from(iteratorOrAsyncIterator);
```

On browsers that don't support the `from()` method you can instead create your own [custom readable stream](/en-US/docs/Web/API/Streams_API/Using_readable_streams#creating_your_own_custom_readable_stream) to achieve the same result:

```js
function iteratorToStream(iterator) {
Expand All @@ -132,8 +143,6 @@ function iteratorToStream(iterator) {
}
```

This works with both async and non-async iterators.

### Async iteration of a stream using for await...of

This example shows how you can process the `fetch()` response using a [`for await...of`](/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) loop to iterate through the arriving chunks.
Expand Down