svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Svelte 5: Runes not tracking async dependencies

Open valterkraemer opened this issue 1 year ago • 8 comments

Describe the bug

A value is only considered a dependency of a rune if it is called in the same event loop tick.

Reproduction

This won't re-evaluate when chatId changes. REPL

let derivedId = $derived(Promise.resolve().then(() => chatId))

but using $: it does work. REPL

$: derivedId = Promise.resolve().then(() => id)

workaround is to e.g. do

let derivedId = $derived.by(() => {
  chatId;
  return Promise.resolve().then(() => chatId);
});

My real life example is that Dexie's liveQuery stopped reacting to changes

let chatMessages = $derived(
  liveQuery(() =>
    localDB.chatMessage
      .where({
        chatId,
      })
      .toArray(),
  ),
);

Logs

No response

System Info

-

Severity

annoyance

valterkraemer avatar May 02 '24 09:05 valterkraemer

This is to be expected. Runes are designed to read dependencies sync only – and given the browser has no native way to track async context (yet), this isn't possible to do otherwise.

trueadm avatar May 02 '24 14:05 trueadm

This is to be expected. Runes are designed to read dependencies sync only – and given the browser has no native way to track async context (yet), this isn't possible to do otherwise.

Thanks, that's what I assumed, but couldn't find any documentation/issue related to it.

I guess the compiler could traverse the inside of e.g. $derived and find all the dependencies though 🤔

valterkraemer avatar May 02 '24 17:05 valterkraemer

@valterkraemer What if you call a function from a random npm module? The compiler is limited in context.

trueadm avatar May 02 '24 17:05 trueadm

@valterkraemer What if you call a function from a random npm module? The compiler is limited in context.

I was thinking just the variables used inside of the $derived block, not calls to code outside of it. But yeah, that's probably not a good idea.

Then this would work

let derivedId = $derived.by(() => {
  return Promise.resolve().then(() => chatId);
});

But not this

function myFunc() {
  return Promise.resolve().then(() => chatId);
}

let derivedId = $derived.by(myFunc);

valterkraemer avatar May 02 '24 17:05 valterkraemer

@valterkraemer That would break far too many cases as we now use runtime reactivity rather than compile time reactivity.

trueadm avatar May 02 '24 17:05 trueadm

Sorry to bother you guys, but what is the idiomatic way to call async methods in new Svelte 5 runes?

frederikhors avatar Aug 28 '24 21:08 frederikhors