Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

{#await expression catch error} control structure for #await block #3623

Closed
rodoch opened this issue Sep 26, 2019 · 13 comments · Fixed by #5682
Closed

{#await expression catch error} control structure for #await block #3623

rodoch opened this issue Sep 26, 2019 · 13 comments · Fixed by #5682
Labels

Comments

@rodoch
Copy link

rodoch commented Sep 26, 2019

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}

Proposed solution

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}

Possible alternatives

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!

@Conduitry
Copy link
Member

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.

@swyxio
Copy link
Contributor

swyxio commented Sep 27, 2019

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}

@rodoch
Copy link
Author

rodoch commented Sep 27, 2019

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.

@swyxio
Copy link
Contributor

swyxio commented Sep 27, 2019

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

@pngwn
Copy link
Member

pngwn commented Sep 27, 2019

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.

@rodoch
Copy link
Author

rodoch commented Sep 27, 2019

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.

@Conduitry
Copy link
Member

#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.

@rodoch
Copy link
Author

rodoch commented Oct 19, 2019

This is a nice compromise on the above issue, cheers!

@frederikhors
Copy link

Shall we update the docs?

@Conduitry
Copy link
Member

The {#await ... catch ...} syntax is now supported in 3.21.0.

@swyxio
Copy link
Contributor

swyxio commented Apr 24, 2020

oo TIL! ah but i think @rodoch's original issue was more about sharing error states somehow

@evdama
Copy link

evdama commented Nov 17, 2020

Shall we update the docs?

{#await ... catch ...} is not documented https://svelte.dev/docs#await 🤔

@tanhauhau
Copy link
Member

@evdama yup, do you want to help update the docs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants