There are cases where a dev may use data returned from a single promise in multiple places within a template. In the event of an error, it may be desirable to display an error message in a single location rather than in catch scope every place there is an #await block associated with that promise. Currently, the Svelte syntax does not make this easy to do. For example:
<script>
let promise = fetchSomeData();
</script>
{#await promise}
<p>waiting for the promise to resolve...</p>
{:then value}
<p>The value is {value.firstPart}</p>
{:catch error}
<p>Something went wrong: {error.message}</p>
{/await}
...
{#await promise}
<p>waiting for the promise to resolve...</p>
{:then value}
<p>The value is {value.secondPart}</p>
{:catch error}
<p>I'm starting to repeat myself: {error.message}</p>
{/await}
Svelte currently provides three control structures within an #await block:
{#await expression}...{:then name}...{:catch name}...{/await}
{#await expression}...{:then name}...{/await}
{#await expression then name}...{/await}
I propose adding a fourth:
{#await expression catch name}...{/await}
In the event that no error is thrown the content within this structure would never be rendered. This would allow you to address the problem as below:
<script>
let promise = fetchSomeData();
</script>
{#await promise catch error}
<p>You're not going to see anything below because of {error.message}</p>
{/await}
...
{#await promise}
<p>waiting for the promise to resolve...</p>
{:then value}
<p>The value is {value.firstPart}</p>
{/await}
...
{#await promise}
<p>waiting for the promise to resolve...</p>
{:then value}
<p>The value is {value.secondPart}</p>
{/await}
I considered whether this could already be achieved in some manner within Svelte. However, a structure such as the following currently returns no output under any conditions:
{#await promise}
{:catch error}
<p>{error.message} in one place</p>
{/await}
That's probably a good thing as such a structure is not very idiomatic. To achieve something that's comparable with the proposed solution above you have to insert empty tags or insert some content for the sake of it, which is also not ideal:
{#await promise}
<p></p>
{:then value}
<p></p>
{:catch error}
<p>{error.message} in one place</p>
{/await}
This is not a make-or-break feature for me but would be a nice addition to Svelte, I suspect it would be unlikely to clash with other features, and would make the syntax even more expressive.
Thanks for the great library!
I would expect something like
{#await Promise.reject('foo')}
{:catch error}
<p>{error}</p>
{/await}
to work, but it looks like that's not getting parsed correctly. I think we should support at least that, but your proposed new syntax might also be nice.
im not sold that we need this yet. you can wrap a promise:
<script>
let promise = fetchSomeData();
let err
let wrapped = () => promise().catch(e => err = e)
</script>
{#if err}
<p>You're not going to see anything below because of {err.message}</p>
{/if}
{#await wrapped}
<p>waiting for the promise to resolve...</p>
{:then value}
<p>The value is {value.firstPart}</p>
{/await}
...
{#await wrapped}
<p>waiting for the promise to resolve...</p>
{:then value}
<p>The value is {value.secondPart}</p>
{/await}
That's a great pointer there @sw-yx, thanks! It certainly works.
If I was to make one more pitch for the proposal above, it's that it offers an idiomatic way to handle this type of situation, or so it appears to me. It avoids explicitly declaring an additional variable in the parent scope for the purposes of error handling. The lack of built-in syntax for a general-purpose error handling scenario (such as a general warning at the top of a form where several elements are async rendered with dynamic data; you want to let people know if the data failed to load for some reason) feels like a gap in the control structures available. These types of situations are common.
I'm more than happy to bow to the collective wisdom of the Svelte community, however, if you think it's an unnecessary feature.
i've not been around long enough to know either. my sense is its a tough call. #await is already unnecessary sugar, but it's one i happen to like. yet you can invent syntax all the livelong day but it adds maintenance and learning debt and will always be behind real world usecases. for example if i want to log my errors to sentry or logrocket i'll have to wrap the promise anyway. so at some point you have to stop creating syntax and just teach code patterns
I'd be inclined to wrap this all up in a store, I don't think expanding the await syntax is really the answer because, as @sw-yx said, you could do this forever. There will always be cases that the await syntax doesn't handle due to the complexities of working with promises/ network requests in the real world. This requires a more comprehensive abstraction in my opinion.
You could do something like this and then you're free to use those values wherever you want, even in other components if that tickles your fancy.
Ok, great discussion, thanks everyone. I've said my piece and strong counter-arguments have been put forward. I'll leave it up to @Conduitry or whomever makes the executive decisions to decide what to do - feel free to close the issue if you think it has been resolved.
{#await} with a {:catch} and no {:then}. I don't think we really need another form {#await expression catch error} probably, so I'm closing this.This is a nice compromise on the above issue, cheers!
Shall we update the docs?
The {#await ... catch ...} syntax is now supported in 3.21.0.
oo TIL! ah but i think @rodoch's original issue was more about sharing error states somehow
Shall we update the docs?
{#await ... catch ...} is not documented https://svelte.dev/docs#await 馃
@evdama yup, do you want to help update the docs?
Most helpful comment
3734 fixes up the parsing here, and lets you use
{#await}with a{:catch}and no{:then}. I don't think we really need another form{#await expression catch error}probably, so I'm closing this.