Svelte 3, async onMount or a valid alternative?


What I need is to use async-await in Svelte onMount().

Or maybe you can suggest me what is wrong and what I can use alternatively.

To Reproduce

  1. go here: https://svelte.dev/repl/000ae69c0fe14d9483678d4ace874726?version=3.23.0
  2. open the console
  3. click on the button
  4. you should see messages: "Mounting..." and "A lot of background work..."
  5. if you click again the destroy message is not written

WHY?

Did onMount() recognizes the async function promise? Should it?

I need that async behavior because I need to wait for function lazyLoading() before rendering the Child component.

Is there an alternative way to do this in Svelte?

Just to explain why onMount can't be an async function (this might change in future, but don't expect it to):

You can return a function from an onMount handler that is called when the component is destroyed. But async functions can only return a promise. Since a promise isn't a function, Svelte will ignore the return value.

This is the same as useEffect in React, incidentally — the function must be synchronous in order to avoid race conditions. The recommended solution for onMount is the same as for useEffect — place an async function inside the handler:

onMount(() => {
  async function foo() {
    bar = await baz();
  }

  foo();

  return () => console.log('destroyed');
});

(Note that you're responsible for handling any race conditions that arise as a result of the component being destroyed before the promise resolves, though assigning state inside a destroyed component is harmless.)

I've opened an issue to discuss providing more useful feedback in these situations: https://github.com/sveltejs/svelte/issues/4944


onMount must be synchronous. However, you can use an {#await} block in your markup and make lazyLoading async, for example:

{#await lazyLoading() then data}
  I'm the child and I loaded "{data}".
{/await}

You could also do...

<script>
  let dataPromise = lazyLoading()
</script>

{#await dataPromise then data}
  I'm the child and I loaded "{data}".
{/await}

See my working example here.

This has the additional benefit of allowing you to use a loader as well as markup that appears when the promise is rejected, using this syntax:

{#await promise}
  loading
{:then value}
  loaded {value}
{:catch error}
  failed with {error}
{/await}