Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

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."
```