Store is not being updated synchronously / store value is incorrect
See original GitHub issueDescribe the bug
Store value is not up-to-date if using a $
subscription inside a subscription function.
To Reproduce
- Open this REPL.
- Open your browser console.
- Click on “Increment” a few times.
- Click on “Cause reset”.
- Check your console.
For a real life example look at this REPL.
Expected behavior Value should be in sync.
Severity Blocking
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:17 (6 by maintainers)
Top Results From Across the Web
Redux store doesn't get updated synchronously?
Yes, the state updates are synchronous by default unless you are using a middleware like thunk . What's happening here is that, ...
Read more >Pragma statements supported by SQLite
This pragma works like a query to return one row for each database attached to the current database connection. The second column is...
Read more >Update-StoreMailboxState (ExchangePowerShell)
The Update-StoreMailboxState cmdlet forces the mailbox store state in the Exchange store to be synchronized with Active Directory.
Read more >Synchronization issues - Google Workspace Learning Center
Google Workspace Sync for Microsoft OutlookHere's how to troubleshoot synchronization issues ... Getting sync errors or synchronization stopped altogether.
Read more >How to use promises - Learn web development | MDN
You will have to reload the page each time you enter a new ... .io/learning-area/javascript/apis/fetching-data/can-store/not-found: 404 ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
First of all,
store.set(value)
is synchronous.if you call
store.set(5)
, you should be able to read the value out of the store immediately,get(store) === 5
.Secondly, the magic of
$store
is:$store
is another variable that stays in-sync with the store value by subscribing to itin most cases, the subscribe callback function is called synchronously, so you can do:
so what happened in the above code is that:
store.set(5)
will synchronously loop through the subscribers function and call them(value) => $store = value)
is evaluated, therefore the value of$store
updated to5
store.set(...)
method returns, and …console.log($store)
prints out5
allow me to break down what happen instead, if you write
$store = 5
, as some of you are confused and wonder what’s the difference betweenstore.set(...)
and$store = ...
when you write
$store = 5
in a.svelte
component, it is compiled into:$store = 5
updates the variable$store
to 5$store = 5
returns5
, thereforestore.set(5)
store.set(5)
will synchronously loop through the subscribers function and call them(value) => $store = value)
is evaluated, therefore the value of$store
updated to5
, though at this point, it is already5
store.set(...)
method returnsstrictly speaking, in this case, the value of
$store
gets update, even before value of thestore
gets update, but it all happen within the same statement, it is unlikely to have a race condition.Now, if we are all aligned with the fundamentals of
store
, here is what introduced this bug:https://github.com/sveltejs/svelte/pull/3219
which introduced an optimisation to update store value via
breadth-first
approach, vsdepth-first
approach, as explained in https://github.com/sveltejs/svelte/pull/3219#issuecomment-515444121how would that impact in our case? let’s take a look at the following example:
repl
we subscribe the store and call
store.set()
in the subscriber function to update the store value to10
, (it wont lead to infinite loop, because internally,writable
will not notify the subscribers if the value is set to the same value), so you set it to30
, it will set it to10
and then it will try to set it to10
again, which will be a noop.guess what is the value of
$store
insidestore.subscribe(...)
? you’ll see the function being called a few times, but you’ll find30
is printed among them!so, if the store is updated in
depth-first
approach, whenever you callstore.set(...)
it will call the subscriber callback function immediately, and update the value of the$store
:store.set(30)
(value) => $store = value
to update the$store
to30
() => { ... }
store.set(10)
(value) => $store = value
to update the$store
to10
() => { ... }
store.set(10)
10
, so it is a noop.console.log($store)
prints out10
console.log($store)
prints out10
Run the above code in
v3.6.8
which was before the optimisation got introduced, and you’ll see the above REPLHowever, now it is run in
breadth-first
approach:store.set(30)
(value) => $store = value
to update the$store
to30
() => { ... }
store.set(10)
(value) => $store = value
() => { ... }
console.log($store)
prints out30
(value) => $store = value
to update the$store
to10
() => {...}
store.set(10)
10
, so it is a noop.console.log($store)
prints out10
now see that the inner
store.set(10)
schedules the update of the variable$store
, therefore you see$store
=30
?that’s exactly what happened in this issue:
calling
random.set()
, which in the subscribe function callscount.set(0)
-> causes the(value) => $count = value
get scheduled, therefore printing the value out immediately still show the old$count
value.but if you do:
you’ll see 0, since
random.set()
started the store update chain, when it finishes, the value of$random
and$count
should already been updated.so, what then?
Workaround / Solution
Use reactive declaration:
@jhwheeler as pushkine said, when you use assignment Svelte assumes that assignment is what you want and sets the store value immediately, regardless of what then happens in
set()
. Callingset()
directly avoids this behavior (might be desirable for custom stores), though frankly I think you’d usually be better off naming the method something else and avoiding the confusion of it also being called after assignment.I was confused enough by this to ask about it on Stackoverflow. I agree with antony; I would have expected
set()
and assignment to have the same behavior by default.