Sapper: Feature Request: Hydratable {#await} blocks

Created on 21 Oct 2019  路  6Comments  路  Source: sveltejs/sapper

Is your feature request related to a problem? Please describe.
When using {#await}, Sapper doesn't seem to wait at all for the result of the promise, and always renders the the placeholder part.

In the below example, Hello darkness. initially appears on the page, and is then replaced by Hello world., potentially confusing the visitor.

{#await Promise.resolve("world")}
  darkness.
{:then result}
  {result}.
{/await}

Describe the solution you'd like
It would be amazing if Sapper was able to resolve the promises server-side so that the browser doesn't even need to execute them again.

I'm currently thinking how this behavior could be implemented, or an elegant work-around. No ideas so far...

hydration proposal

Most helpful comment

Right. I'm just thinking about how to make common use cases easier to implement. Consider this (I think rather common case):

  • A search box and results are displayed on the page
  • Whenever the user inputs a new searchTerm, a loading animation should start playing.
  • When the search url query parameter is present, it should server-side render the results for that search term for better SEO.

My current solution works but feels really awkward.

<script context="module">
async function fetchData(searchTerm) {
    return Promise.resolve('Results for ' + searchTerm)
}

export async function preload(page) {
    return { 
        results: await fetchData(page.query.search) 
    };
}
</script>

<script>
import {stores} from '@sapper/app';
const {page} = stores();
export let results;
let searchTerm = $page.query.search;

// If we run in SSR mode, we don't want to start an async process
$: results = typeof window == 'undefined' ? results : fetchData(searchTerm)

</script>

<input bind:value={searchTerm} type="text">
{#await results}
  Nice loading animation
{:then data}
  {data}
{/await}

I was hoping to get this down to:

<script>
import {stores} from '@sapper/app';
const {page} = stores();
let searchTerm = $page.query.search;
</script>

<input bind:value={searchTerm} type="text">
{#await fetchData(searchTerm)}
  Nice loading animation
{:then data}
  {data}
{/await}

This just _feels_ like it should be possible.

All 6 comments

This isn't possible with Svelte in its current form, because SSR is always synchronous. This is part of what preload is for - it lets you run asynchronous code before the component is even instantiated.

Right. I'm just thinking about how to make common use cases easier to implement. Consider this (I think rather common case):

  • A search box and results are displayed on the page
  • Whenever the user inputs a new searchTerm, a loading animation should start playing.
  • When the search url query parameter is present, it should server-side render the results for that search term for better SEO.

My current solution works but feels really awkward.

<script context="module">
async function fetchData(searchTerm) {
    return Promise.resolve('Results for ' + searchTerm)
}

export async function preload(page) {
    return { 
        results: await fetchData(page.query.search) 
    };
}
</script>

<script>
import {stores} from '@sapper/app';
const {page} = stores();
export let results;
let searchTerm = $page.query.search;

// If we run in SSR mode, we don't want to start an async process
$: results = typeof window == 'undefined' ? results : fetchData(searchTerm)

</script>

<input bind:value={searchTerm} type="text">
{#await results}
  Nice loading animation
{:then data}
  {data}
{/await}

I was hoping to get this down to:

<script>
import {stores} from '@sapper/app';
const {page} = stores();
let searchTerm = $page.query.search;
</script>

<input bind:value={searchTerm} type="text">
{#await fetchData(searchTerm)}
  Nice loading animation
{:then data}
  {data}
{/await}

This just _feels_ like it should be possible.

I wonder if it's possible to modify the SSR compiler to produce async code. So that the produced code actually executes all promises it encountered in {#await} blocks, and uses the results to render the page.

The entire render path would need to be async 馃し

Routify has a way to make this work using the $ready() function. Maybe Sapper could allow implement similar? You know, just 2 ways of declaring when you finish SSR and send the results to the browser?

I wonder if it's possible to modify the SSR compiler to produce async code. So that the produced code actually executes all promises it encountered in {#await} blocks, and uses the results to render the page.

I would want to go a slightly different route. In my use-case the promise that I'm awaiting is indeed already resolved, just like in your example. As in I made sure that the promise got resolved in the preload() function. So sapper would literally only need to "wait" one clock tick in order to get the results in the {#await} block.

So I would just want to ask Sapper to wait 1 clock tick if it happens to find an {#await} block in the template.

@Conduitry is that more realistic to implement?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Rich-Harris picture Rich-Harris  路  3Comments

Rich-Harris picture Rich-Harris  路  3Comments

benmccann picture benmccann  路  3Comments

Snugug picture Snugug  路  4Comments

Rich-Harris picture Rich-Harris  路  3Comments