Skip to content

Conversation

Rich-Harris
Copy link
Member

@Rich-Harris Rich-Harris commented Sep 25, 2025

The $effect.pending rune tells you how many await expressions are unresolved in the current boundary, which is useful for providing feedback to the user that something is happening.

This PR augments it: when you pass an expression to it, it will always return the current value of that expression.

Quick primer: when a piece of state changes, and an await expression depends on that state, Svelte doesn't update the UI to reflect the state change until the await expression has resolved. Otherwise it's a total free-for-all — if you have one component that does await fetchPost(params.slug) and another one that does await fetchComments(params.slug), the post and the comments will be rendered at different times. Or, to take a contrived example that makes it clear, in a case like this...

<p>1 * {n} = {await multiply(n, 1)}</p>

...if n changes from 1 to 2 the UI will show 1 * 2 = 1. This is a bad default.

Other frameworks solve this with things like useTransition. Svelte's view is that you want to use a transition in 99% of cases, and so making it opt-in rather than opt-out is the wrong choice. But right now we don't have an opt-out mechanism.

$effect.pending(...) is that mechanism. It allows you to always show the latest version in one specific part of your UI — for example, in the button that changes some state.

Caveats:

  • Not battle-tested
  • I haven't discussed this API with anyone, I just wanted to try it out. Maybe it's terrible!
  • Because of how it works, it has to be lexically within an effect — we have to create a signal that wraps the value and hoist that signal out of the effect in which it's used. For the template that's fine, but if we're to make it possible to use this inside $effect and $effect.pre, it can't be inside a function that's called from the effect, it has to be 'visible' to the compiler. (Right now, it only works in the template. And maybe that's fine?)

Before submitting the PR, please make sure you do the following

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • Prefix your PR title with feat:, fix:, chore:, or docs:.
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.
  • If this PR changes code within packages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • Run the tests with pnpm test and lint the project with pnpm lint

Copy link

changeset-bot bot commented Sep 25, 2025

⚠️ No Changeset found

Latest commit: 21811b1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

Playground

pnpm add https://pkg.pr.new/svelte@16849

@Thiagolino8
Copy link

The problem with only being able to use it in the template is that you need to be vigilant about using it in all places your state shown if you want to avoid tearing of displays of the same state
In your example, the counter has one value on the button and another in the template
In my opinion, tearing within the same state is much worse than tearing between a state and its derivatives
It seems to me like something that would easily generate inconsistent UIs.
And currently there's an error if you try to use it more than once

@dummdidumm
Copy link
Member

dummdidumm commented Sep 26, 2025

yeah it 100% must be usable in the script tag aswell (e.g. inside $derived)

@Rich-Harris
Copy link
Member Author

People have doubts about the name. So do I. I reused $effect.pending() because it was lying around already, but it doesn't do a great job of communicating what this is. And since we go out of our way to discourage the use of $effect, it might be confusing to people that we're promoting this as the way to show immediate feedback upon changes.

So what should we rename it, and should we also rename $effect.pending()? $state.pending()? $state.latest(...)?

@7nik
Copy link
Contributor

7nik commented Sep 26, 2025

I'm thinking of nonblocking, immediate, and latest.

@Ocean-OS
Copy link
Member

$derived.latest would be great if we restricted the expression to be pure. Even though it doesn't function the same as a $derived under the hood, it seems pretty similar to me.

@Ocean-OS
Copy link
Member

I think I've found a bug: if you click the counter fast enough the text can show incorrect results.
Screenshot_20250929_160324_Chrome.jpg

@Serator
Copy link

Serator commented Sep 30, 2025

I encountered a similar problem. Should this be moved to a separate issue?

iShot_2025-09-30_10.14.18.mp4

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

Successfully merging this pull request may close these issues.

6 participants