# Simple debouncer

**URL:** <https://discourse.elm-lang.org/t/simple-debouncer/5947>\
**Category:** Show and Tell\
**Created:** [June 21, 2020, 12:12pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947 "2020-06-21T12:12:14Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Grotsev](https://avatars.discourse-cdn.com/v4/letter/g/898d66/32.png) [@Grotsev](https://discourse.elm-lang.org/u/Grotsev)\
**Post date:** [June 21, 2020, 12:12pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/1 "2020-06-21T12:12:14Z")

</div>

It’s common to debounce user input and do some action (e.g. HTTP request) after it is settled. For instance, request autocompletion from server or check login is already registered. There are another more complex time-throttling Elm libraries. [My new library](https://package.elm-lang.org/packages/grotsev/elm-debouncer/latest/) is the most simple one. You may understand how it’s work from the first glance.

---

<div class="post-metadata">

**Author:** ![Lucas\_Payr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lucas_payr/32/4993_2.png) [@Lucas\_Payr](https://discourse.elm-lang.org/u/Lucas_Payr)\
**Post date:** [June 21, 2020, 1:04pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/2 "2020-06-21T13:04:14Z")

</div>

I read through your source code and found it a bit confusing. For example am I right to believe that `push` and `pop` are meant to increase and decrease time? So why are they not named `addTime` and `removeTime` or something similar?

I tried to come up with a very simple solution once before, but my solution is not as minimalistic as yours:

> [@Example: Debounced Validation](https://discourse.elm-lang.org/t/example-debounced-validation/3804):
>
> Hi everyone. I’m currently working on an open source collection of useful code snippets and good practices, over at [Orasund/elm-cookbook](https://orasund.gitbook.io/elm-cookbook/). I recently got [asked](https://github.com/Orasund/elm-cookbook/issues/1) to add an example for how to debounce a form validation: The validation should only fire if the user has not typed for 500ms. I’m definitely no expert in that field and I don’t want to reinvent the wheel. That’s why I’m asking for feedback. After looking at a two examples ([a gist by jcaxmacher](https://gist.github.com/jcaxmacher/796e21c0853ea7c85b3aa5d1307c0a4c) and the [elm seed example](https://elmseeds.thaterikperson.com/)), I came up wit…

I like the idea of the `Bounce` type essentially being a counter a lot. This really sparked my interest in this topic again. But as it is right now, I feel like the tricky parts to debouncing are left a bit untouched with your package.

---

<div class="post-metadata">

**Author:** ![Grotsev](https://avatars.discourse-cdn.com/v4/letter/g/898d66/32.png) [@Grotsev](https://discourse.elm-lang.org/u/Grotsev)\
**Post date:** [June 21, 2020, 5:12pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/3 "2020-06-21T17:12:41Z")

</div>

Let’s suppose Alice and Bob are in the garden. Alice shoot arrows. Bob is sunbathing.While arrow is in flight it is not safe to sunbath. It doesn’t matter how long time arrow flies if Bob counts launches (`push`) and landings (`pop`). When `Bounce` counter is zero, there is no arrows in flight, or equivalently, flight time is elapsed for every arrow. So, there is no explicit concept of time in `Bounce` counter.

I’ve tried another debounce libraries and they are too complex for form validation. It’s interesting, what tricky parts do you mean?

---

<div class="post-metadata">

**Author:** ![Lucas\_Payr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lucas_payr/32/4993_2.png) [@Lucas\_Payr](https://discourse.elm-lang.org/u/Lucas_Payr)\
**Post date:** [June 21, 2020, 5:54pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/4 "2020-06-21T17:54:17Z")

</div>

I can’t quite adapt your analogy to filling in a form. So Bob is the server and Alice is the user? What are the arrows? Also in your Analogy, Bob shouldn’t count the arrows, instead he should count the time while no arrows are in sight (as he might start sunbathing a bit to soon and get shoot by a late arrow)

If you look into the thread that I have posted, you see the original problem specification:

The Form contains a “username”, a “password” and a “passwordRepeat” field.

- The server should check if the username is free
- The server should check if the two passwords are the same and if the password is contained in the list of unsave passwords

To do so the following should hold true:

- The server will wait until the user has stopped typing within 500ms.
- If the user starts typing again, the server request should be aborted
- Switching between input fields should be considered
  - Switching to one input and back to the other within the 500ms should not trigger an abort Event
  - No data may be lost during switching

---

<div class="post-metadata">

**Author:** ![Grotsev](https://avatars.discourse-cdn.com/v4/letter/g/898d66/32.png) [@Grotsev](https://discourse.elm-lang.org/u/Grotsev)\
**Post date:** [June 21, 2020, 7:21pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/5 "2020-06-21T19:21:48Z")

</div>

Yea! Bob might start safe sunbathing (or send HTTP request to server) as soon as last arrow is landed, i.e. there is 0 arrow in flight. Alice launch arrow (press key) and `Bounce.delay` its landing. At launch moment Bob `Bounce.push`, at landing moment `Bounce.pop`

Probably, analogy is controversial, but code is monosemantic.  
You may try `elm reactor` on  
[example](https://github.com/grotsev/elm-debouncer/blob/master/examples/Example.elm) where request is sent to the server after 1000ms of user idle. And you may use `Bounce` as building block in your solution. But HTTP request cancelation is out of scope of the debouncing library.

---

<div class="post-metadata">

**Author:** ![Lucas\_Payr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lucas_payr/32/4993_2.png) [@Lucas\_Payr](https://discourse.elm-lang.org/u/Lucas_Payr)\
**Post date:** [June 21, 2020, 7:35pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/6 "2020-06-21T19:35:46Z")

</div>

🤔 Okay, so in your example it seems like you are counting the key presses. And for every key-press you wait specified amount of time. So longer messages will have a longer debounce time than shorter ones.

Now I’m getting your analogy. Interesting.

---

<div class="post-metadata">

**Author:** ![Laurent](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/laurent/32/5725_2.png) [@Laurent](https://discourse.elm-lang.org/u/Laurent)\
**Post date:** [June 23, 2020, 5:19am UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/7 "2020-06-23T05:19:20Z")

</div>

I simply add a delayed message step to call my command only if the input is still the same after the delay. It also allows me to easily add some validation (not included in the example below for readability).

```
delayMsg : msg -> Cmd msg
delayMsg msg =
    Task.perform (always msg) (Process.sleep 1000)

-- in your update cases:

GotFieldInput fieldInput ->
    ( { model | fieldInput = fieldInput }
    , delayMsg (GotDelayedFieldInput fieldInput)
    )

GotDelayedFieldInput fieldInput ->
    if fieldInput == model.fieldInput then
        ( { model | status = Loading }, costlyCommand fieldInput )
    else
        ( model, Cmd.none )

```

I don’t see how it could be simpler 😉

---

<div class="post-metadata">

**Author:** ![Grotsev](https://avatars.discourse-cdn.com/v4/letter/g/898d66/32.png) [@Grotsev](https://discourse.elm-lang.org/u/Grotsev)\
**Post date:** [June 23, 2020, 7:18am UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/8 "2020-06-23T07:18:55Z")

</div>

Too simple to be correct 😉. There are some cases this solution emits redundant commands when user use backspace or copypaste.

Counter example is infinite sequence of `fieldInput` values `"a", "ab", "a", "ab", "a", "ab" ...`. User is always typing but command is emmitted with 50% probability.

---

<div class="post-metadata">

**Author:** ![Laurent](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/laurent/32/5725_2.png) [@Laurent](https://discourse.elm-lang.org/u/Laurent)\
**Post date:** [June 23, 2020, 7:57am UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/9 "2020-06-23T07:57:18Z")

</div>

Sure, but

1. the counter example will never occur in real life
2. if more commands are triggered that _should_ be no big deal, well hopefully for something that happens at key strokes 😉

---

<div class="post-metadata">

**Author:** ![Grotsev](https://avatars.discourse-cdn.com/v4/letter/g/898d66/32.png) [@Grotsev](https://discourse.elm-lang.org/u/Grotsev)\
**Post date:** [June 23, 2020, 8:24am UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/10 "2020-06-23T08:24:26Z")

</div>

1. And one moment you find yourself in deep debugging.
2. Unreliable approach is not composable. But we may with ease make up more complex systems from reliable `Bounce`. For instance, wait while user is typing or moving mouse. Or show spinner while any of two HTTP requests are loading.

---

<div class="post-metadata">

**Author:** ![Grotsev](https://avatars.discourse-cdn.com/v4/letter/g/898d66/32.png) [@Grotsev](https://discourse.elm-lang.org/u/Grotsev)\
**Post date:** [June 23, 2020, 8:27am UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/11 "2020-06-23T08:27:32Z")

</div>

Interpret `Bounce` as synchronization primitive.

---

<div class="post-metadata">

**Author:** ![Laurent](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/laurent/32/5725_2.png) [@Laurent](https://discourse.elm-lang.org/u/Laurent)\
**Post date:** [June 23, 2020, 10:21am UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/12 "2020-06-23T10:21:39Z")

</div>

1. As I’m simply using messages no issues with debugging thanks to The Elm Architecture.
2. I’m using spinners in my debounced forms (see the `Loading` status of the simplified example), it is quite easy to add too.

Do you mean your `Bounce` is chainable?

It’s great that you made a debounce library. Just like you I found debounce libs to be unnecessarily complex. That’s why for ~~simple~~ unexceptional cases I came to use this `delayMsg` way. Plus it’s nice to have no dependencies 😉

---

<div class="post-metadata">

**Author:** ![Grotsev](https://avatars.discourse-cdn.com/v4/letter/g/898d66/32.png) [@Grotsev](https://discourse.elm-lang.org/u/Grotsev)\
**Post date:** [June 23, 2020, 12:06pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/13 "2020-06-23T12:06:21Z")

</div>

1. Depends on problem you solve. Elm Architecture solves event loop dispatching problem, it doesn’t solve business logic state management problems.
2. I guess, you show one spinner for each field. And I mean, we have to show one spinner for all fields together. Or enable Send button as soon as all fields are validated on the server.

`Bounce` is not just chainable/`andThen`able (associative), it is commutative (it is just Int counter). So, messages can arrive in any order with different delays. Your solution commutative too, but it say "`fieldInput` is same as 1 second ago". It does not say “user is not typing”.

My library is not rocket science 😆 just increment and decrement one can do themselves without dependency. But this pattern turns out surprisingly useful. There were earlier versions of solution where I had sent timestamp or value as you, but they were not so robust and simple.

Thanks for discussion 😃 It helps to improve unclear lib docs motivation and add more expressive examples.

---

<div class="post-metadata">

**Author:** ![cmditch](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/cmditch/32/952_2.png) [@cmditch](https://discourse.elm-lang.org/u/cmditch)\
**Post date:** [June 24, 2020, 4:33pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/14 "2020-06-24T16:33:29Z")

</div>

I hacked up a combined Debounce + Throttler library a month or so ago, which I’ll probably publish v3ery soon here.

(Not to discount your original post @Grotsev, but just in case you or anyone else here finds this approach handy.)

The advantages of the approach I’ve taken are:

- Can be used as just a debouncer, just a throttler, or both.

- Provides Google Docs style saving behavior, e.g. I want to save 1 second after the user stops typing, but also save at least every 6 seconds in case they continually type.

- A single `Debouncer msg` in your model (for a given debounce/throttle time). No need to instantiate a separate debouncer for every thing you need debounced.

- A very simple API, `poke` & `cancel` (excluding the standard TEA `init` & `update`)

- `Msg` being fired can be changed mid-debounce/throttle, e.g. incase a validation error suddenly appears.

- Only 125 lines of code

The downside is it’s not 100% accurate to the millisecond, which I think is fine for cases of human interaction. e.g. you may have a debounce of 1 second, but it actually debounces at 1.3 seconds. This is due to how `Msg.Check` works.

> <https://gist.github.com/cmditch/4d8df9fdb8985c8b1a17a26a0b886a82>

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [July 4, 2020, 4:33pm UTC](https://discourse.elm-lang.org/t/simple-debouncer/5947/15 "2020-07-04T16:33:30Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
