Skip to content

chore: let async derived coordinate batches#17955

Draft
dummdidumm wants to merge 10 commits intomainfrom
async-derived-coordinate-batches
Draft

chore: let async derived coordinate batches#17955
dummdidumm wants to merge 10 commits intomainfrom
async-derived-coordinate-batches

Conversation

@dummdidumm
Copy link
Member

Instead of having batches coordinate whether they should block on other batches, or rebase earlier/later ones, we move the coordination into the async deriveds.

More concretely, we always run async operations in order on the async_derived level. During function invocation we temporarily ignore batch_values so that the latest value across all batches is retrieved (to get that working properly we may need to execute deriveds twice to get the value in the context of batch_values and the latest value across all batches). Because we do that we have to wait on prior runs to resolve before resolving ours, except when the batch in question is a subset of the current batch; in that case we can resolve directly and reject the async derived of the other batch as stale. This combination of things (latest value across all batches but resolving in order except when we don't need to wait) allows us to get rid of the batch.#commit() logic.

This makes all existing tests pass (only one needed to be adjusted) but one new test and one adjusted test in #17162 fail here.

Instead of having batches coordinate whether they should block on other batches, or rebase earlier/later ones, we move the coordination into the async deriveds.

More concretely, we always run async operations in order on the async_derived level. During function invocation we temporarily remove batch_values so that the latest value across all batches is retrieved. Because we do that we have to wait on prior runs to resolve before resolving ours, except when the batch in question is a subset of the current batch; in that case we can resolve directly and reject the async derived of the other batch as stale. This combination of things (latest value across all batches but resolving in order except when we don't need to wait) allows us to get rid of the `batch.#commit()` logic.
@changeset-bot
Copy link

changeset-bot bot commented Mar 17, 2026

⚠️ No Changeset found

Latest commit: c879d5c

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

@github-actions
Copy link
Contributor

Playground

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

@svelte-docs-bot
Copy link

*/
function is_batch_subset(subset, superset) {
for (const source of subset.current.keys()) {
if ((source.f & DERIVED) !== 0) continue;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

his probably also needs the "this is a writable derived" differentiation

} finally {
set_ignore_batch_values(previous_ignore_batch_values);
set_has_batch_value_differences(previous_has_batch_value_differences);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this part of the PR is useful outside of it, too

Comment on lines +366 to +367
// ASYNC effects should read canonical signal values instead of batch overlays
// while they run.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this logic should live in async_derived?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wait I see it already does? do we need both?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be we no longer need both / only this. fear was that we could rerun async deriveds at a different point that this one.
We definitely need this part because is_dirty can execute deriveds and those already need to compute/return the correct values.

Comment on lines +21 to +25
<button>a</button>
<button>b</button>
<button>resolve</button>
hi
1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is going on with the indentation here 😆

@Rich-Harris
Copy link
Member

Unfortunately the async-overlapping-array test from #17162 fails on this branch, because the each block effect runs with an isolated batch_values containing an array like [1, undefined, undefined, 4] instead of [1, 2, 3, 4], which results in duplicate keys. So I'm not sure we can treat async deriveds as the 'choke point' for this to work

@dummdidumm dummdidumm marked this pull request as draft March 18, 2026 15:58
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.

2 participants