Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dgp1130/out-of-order-streaming
Demos out of order streaming with declarative shadow DOM
https://github.com/dgp1130/out-of-order-streaming
Last synced: 13 days ago
JSON representation
Demos out of order streaming with declarative shadow DOM
- Host: GitHub
- URL: https://github.com/dgp1130/out-of-order-streaming
- Owner: dgp1130
- Created: 2024-01-07T22:32:54.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2024-01-09T02:05:08.000Z (10 months ago)
- Last Synced: 2024-10-06T02:02:25.113Z (about 1 month ago)
- Language: TypeScript
- Size: 181 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Out of Order Streaming
Experimenting with out-of-order streaming using declarative shadow DOM.
Inspired by: https://mastodon.social/@passle/111709021119627922
## Demo
This demo is hosted at https://dsd-out-of-order-streaming.dwac.dev/. It
bootstraps a service worker and reloads the page. The service worker then takes
over and streams content out of order to the page. No client-side JavaScript!## Algorithm
This works by taking advantage of the fact that declarative shadow DOM puts
light DOM content at the _end_ of a component. Take this example:```html
- First
- Third
Second
```Here, `Second` is moved to the _end_ of the root. Meaning `Third` can be
streamed before it.This demo attempts to make a reusable function to generate this format
automatically. Using it looks like:```typescript
async function* render(): AsyncGenerator {
yield* streamInOrder`
${streamOutOfOrder`
First
${getSecondSlowly()}
Third
`}
`;
}async function getSecondSlowly(): Promise {
await new Promise((resolve) => {
setTimeout(() => { resolve(); }, 1_000);
});return 'Second';
}
````First` and `Third` will be displayed immediately, while `Second` will be
streamed in after a one second timeout.(`streamInOrder` is standard streaming, but necessary to stream the root of the
document which can't leverage this out of order technique.)The two files doing most of the work here are:
* [`src/server.ts`](/src/server.ts) - Uses `streamOutOfOrder`.
* [`src/streaming.ts`](/src/streaming.ts) - Implements `streamOutOfOrder`.### Composition
We can compose multiple `streamOutOfOrder` calls in the same expression like so:
```typescript
async function* render(): AsyncGenerator {
yield* streamInOrder`
${streamOutOfOrder`
${first()}
${streamOutOfOrder`
${second()}
${third()}
`}
${fourth()}
`}
`;
}
```This still works and will parallelize all four operations. They can be rendered
in any order depending on which completes first.This is done by generating a host element for each `streamOutOfOrder` call and
then binding the slots from the outer element to the inner element. The final
HTML looks like:```html
First
Second
Third
Fourth
```## Caveats
This definitely isn't a comprehensive out-of-order streaming solution. There are
a few caveats to be aware of:* Cannot stream `` content out of order. Only `` content makes
sense with DSD.
* Cannot interpolate across a declarative shadow DOM boundary.
* Putting content inside a shadow root creates a new scope of slots.
`streamOutOfOrder` automatically handles this for the shadow roots it
creates, but it cannot detect and adapt shadow roots it is provided.
* Example:
```typescript
streamOutOfOrder`
${content}
`;
```
* A more comprehensive parser could potentially modify input shadow roots
to make this pattern possible.
* This particular implementation creates a number of `` elements, which
can make styling and layout more difficult. A more intelligent parser could
potentially put the shadow roots onto existing rendered elements and avoid
changing the actual realized DOM structure.
* Because each `streamOutOfOrder` call creates a new shadow root, styles are
not inherited in child elements. Shadow DOM is providing style isolation we
don't actually want. Styles would need to be loaded inside each shadow root,
which can get complicated in practice.## Internal
Some more docs related to managing this repository.
### Deployment
The demo site is hosted on Netlify and can be deployed manually with the
following command:```shell
npm run -s netlify -- deploy -s "${SITE_ID}" --prod -m "Manual deployment."
```