kenkunz / svelte-fsm

Tiny, expressive finite state machines for svelte
MIT License
275 stars 9 forks source link

Debounce not bound to state => may yield surprising results #6

Open kju2 opened 2 years ago

kju2 commented 2 years ago

Hi! I really like the simplicity and expressiveness of Svelte-FSM.

While implementing a FSM I tripped over the fact that debounce calls are not bound to the event where they were called from.

<script>
    import fsm from "svelte-fsm";

    const switchWithDelayedAction = fsm("off", {
        off: {
            click: "on",
            doStuff() {
                console.log("Surprise!");
            },
        },
        on: {
            _enter() {
                this.doStuff.debounce(1500);
            },
            click: "off",
            doStuff() {
                console.log("Do stuff in ON state.");
            },
        },
    });
$: console.log("state: ", $switchWithDelayedAction);
</script>

<button on:click={switchWithDelayedAction.click}>Click me</button>

https://svelte.dev/repl/56d96df27fea4d68a179084edc22a3ab?version=3.46.4

If I double click on the button (second click less than 1500 ms after the first šŸ™‚ ), then "doStuff" is called in the "off" state. I expected the "doStuff" method in the "on" state to be called.

A workaround is to call "this.doStuff.debounce(null)" in the "_exit" method of the "on" state. (https://svelte.dev/repl/1b041923ec1e48258f8858c1fd597f46?version=3.46.4)

I see two "simple" solutions (hopefully there are better ones out there šŸ™‚)

fredguth commented 2 years ago

I tried to fast double click the example you sent and couldn't see the described behaviour. I see it now.

kenkunz commented 2 years ago

@kju2 thanks for reporting this, and sorry for not looking at it sooner. I'll look at your example and suggestions and let you my thoughts on this soon.

kenkunz commented 2 years ago

Hi @kju2,

I gave this some thought ā€“Ā I think the current behavior is what I would expect, and allows for more flexible scenarios.

Consider a scenario where you have a multi-step process with each step represented by a state, and where you need to complete all steps before a timer runs out. The timer is started with debounce when you enter step 1. A fallback timesUp action transitions to a outOfTime state. In this scenario, you would not want debounce to be bound the the state in which it was invoked.

An alternative solution you might consider is to pass an additional from argument when you invoke via debounce, then inspect this arg in your handler. See updated example: https://svelte.dev/repl/0ff0912819904880b5808f5269858348?version=3.48.0

Per your recommendation, I will document this behavior to reduce confusion. I'll close the issue once I've updated the docs.

Thank you again for using svelte-fsm and for reporting this!

Regards, Ken