question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Reactive blocks run only once per tick, losing changes and allowing fast pace apps to get out of sync

See original GitHub issue

Describe the bug

TLDR: Reactive blocks that need to stay up to speed with multiple state changes during the same tick basically break the application in subtle, hard to reason about ways (because things “randomly” go out of sync). This bug breaks the basic guarantee of reactivity.

  • This bug goes all the way back to version 3.0.0.
  • I am giving it high severity because at least for us, it is a huge factor in deciding whether to use Svelte for future projects and I think it will be the same for other teams the encounter these behaviours.

More Info and context (skip to the next section for REPLs):

  • My team is using Svelte in production to build some highly graphic and complex interactive experiences (game like), which sometimes mean rapid state updates and ideally a lot of reactive blocks to keep different pieces separate.
  • This bug happens with stores as well, even though store subs behave differently as they do fire multiple times per tick (which is a good thing). This adds to the confusion and overall feeling of inconsistency (people in my team initially thought stores are causing this issue).
  • Anything async also behaves differently (because it is not in the same tick), it leads to consistent state and potentially introduces infinite loops in a surprising manner (because the code “worked just fine” because of this bug when there was no async behaviour) which adds to the confusion and inconsistency of the DX.
  • Because of how puzzling it is when it happens in a complex app (I change state but it doesn’t render), devs in my team were constantly puzzled by why one way of doing something works while another sends the app to hell. We started thinking about reactive blocks as strange foot guns 😢 . Now that I know the root cause we can find (ugly) ways around it by being extra vigilant at all times, but I am sure others will encounter it too.
  • I was told by some on #6730 (I thought it is the same issue at first) that this is a built in protection from possible infinite loops. If that’s the case this is at minimum a bug in the documentation and in my opinion not a good design decision for the framework. App consistency is more important and this defence mechanism is very limited and confusing anyway. It also means svelte is not well suited for the type of apps that could benefit the most from its full feature-set, performance, small size and elegance (== complex apps built by devs who can easily defend from infinite loops with normal code or the conditionals of the reactive blocks).
  • I know that changing this can break existing apps that rely on it. Could this behaviour be made opt-out via some compiler option or otherwise (Ideally something we can pass in via the rollup config)?
  • I am willing to allocate resources to fixing it if that helps (and if we can get some guidance for where to start from)

Reproduction

Expected behaviour: isSmallerThan10 should be false. Actual behaviour: isSmallerThan10 stays true, which is out of sync with the app state (and breaks the contract of reactivity)

Logs

N/A

System Info

System:
    OS: macOS 11.5.2
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 39.63 GB / 64.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 12.16.2 - ~/.nvm/versions/node/v12.16.2/bin/node
    Yarn: 1.22.10 - ~/.nvm/versions/node/v12.16.2/bin/yarn
    npm: 7.16.0 - ~/.nvm/versions/node/v12.16.2/bin/npm
  Browsers:
    Brave Browser: 86.1.15.76
    Chrome: 93.0.4577.82
    Firefox: 89.0.2
    Safari: 14.1.2

Severity

blocking all usage of svelte

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:9
  • Comments:35 (21 by maintainers)

github_iconTop GitHub Comments

6reactions
rmunncommented, Sep 16, 2021

Note that your REPL example can be simplified even further: the same behavior is seen without needing an object property. In other words, when you write let count = {a:1};, you could demonstrate the same behavior even more simply with let count = 1, as follows:

let isSmallerThan10 = true;
let count = 1;
$: if (count) {
  if (count < 10) {
    console.error("smaller", count);
    // this should trigger this reactive block again and enter the "else" but it doesn't
    count = 11; 
  } else {
    console.error("larger", count);
    isSmallerThan10 = false;
  }
}
3reactions
rmunncommented, Sep 25, 2021

It blows my mind a bit because I didn’t know lifecycles can be used outside of components. I guess it works because after compilation there is only the component.

The docs say that onDestroy “Schedules a callback to run immediately before the component is unmounted.” So you can put it inside a helper function and that’s okay, because when that helper function is being run, it’s always being run by a component, and it will add that callback to that component’s onDestroy list.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Screen and Block Lifecycle Events - OutSystems documentation
In OutSystems Mobile and Reactive Web Apps, the Screens and Blocks follow a lifecycle composed by a set of stages.
Read more >
Advanced Svelte: Reactivity, lifecycle, accessibility
In this article we will add the app's final features and further componentize our app. We will learn how to deal with reactivity...
Read more >
Unreal Engine 5.1 Release Notes
Overview of new and updated features in Unreal Engine 5.1.
Read more >
Leading Blog: A Leadership Blog - LeadershipNow.com
STUDIES have shown that humility is one of the most important ... unable to get out of the funk we are in, we...
Read more >
The Brain That Changes Itself - BrainMaster
Neuro is for "neuron," the nerve cells in our brains and nervous systems. Plastic is for "changeable, malleable, modifiable." At first many of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found