README.md

# DebounceAndThrottle

[![Hex version badge](https://img.shields.io/hexpm/v/debounce_and_throttle.svg)](https://hex.pm/packages/debounce_and_throttle)
[![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/johnknott/debounce-and-throttle/blob/master/LICENSE.md)
[![Code coverage badge](https://img.shields.io/codecov/c/github/johnknott/debounce-and-throttle/badge.svg)](https://app.codecov.io/gh/johnknott/debounce-and-throttle)
[![Github Workflow](https://img.shields.io/github/workflow/status/johnknott/debounce-and-throttle/Elixir%20CI?logo=GitHub)](https://github.com/johnknott/debounce-and-throttle/actions/workflows/elixir.yml)

DebounceAndThrottle is a simple library to allow to *debounce* or *throttle* function calls or message sending.

The following page explains throttling and debouncing quite well, albeit from a javascript perspective.

[https://css-tricks.com/debouncing-throttling-explained-examples/]

Examples can be found below and full documentation can be found at [hexdocs.pm](https://hexdocs.pm/debounce_and_throttle/api-reference.html).

## Installation

The package can be installed by adding `debounce_and_throttle` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:debounce_and_throttle, "~> 0.9.0"}
  ]
end
```

## Setup

### Setup with a Supervision Tree, perhaps in a Phoenix project

Add `DebounceAndThrottle.Server` to your Supervision tree in `lib/<application_name>/application.ex`

```elixir
children = [DebounceAndThrottle.Server, ...]
```

### Setup without a Supervision Tree, perhaps in a standalone script

```elixir
DebounceAndThrottle.Server.start_link([])
```

## Usage

Alias Debounce and Throttle if you want to keep things terse.

```elixir
alias DebounceAndThrottle.{Debounce, Throttle}
```

### Debounce

This anonymous function will be called after 5 seconds, but only if this method wasnt called in the interim with the same `key`

```elixir
Debounce.call(fn -> IO.puts("Hey there!") end, "some-key", 5_000)
```

This module, function and arguments will be called after 5 seconds, but only if this method wasnt called in the interim with the same `key`

```elixir
Debounce.apply(IO, :puts, ["Hey there!"], "some-key", 5_000)
```

This message will be sent after 5 seconds, but only if this method wasnt called in the interim with the same `key`

```elixir
Debounce.send(fn -> IO.puts("Hey there!") end, "some-key", 5_000)
```

Returns the state - the current list of debounced functions. Useful for debugging.

```elixir
iex> Debounce.state
%{
  apply: %{},
  call: %{
    "say_hey" => %DebounceAndThrottle.Debounce{
      debounced_count: 1,
      extra_data: %{fun: 'Function<45.65746770/0 in :erl_eval.expr/5>'},
      scheduled_at: ~U[2022-03-12 22:50:01.190171Z],
      timer_ref: 'Reference<0.418177534.3850108929.259344>'
    }
  },
  send: %{}
}
```

### Throttle

This anonymous function will be called straight away, but only if this method wasnt called in the last 5 seconds with the same `key`

```elixir
Throttle.call(fn -> IO.puts("Hey there!") end, "some-key", 5_000)
```

This module, function and arguments will be called straight away, but only if this method wasnt called in last 5 seconds with the same `key`

```elixir
Throttle.apply(IO, :puts, ["Hey there!"], "some-key", 5_000)
```

This message will be sent straight away, but only if this method wasnt called in the last 5 seconds with the same `key`

```elixir
Throttle.send(fn -> IO.puts("Hey there!") end, "some-key", 5_000)
```

Returns the state - the current list of throttled functions. Useful for debugging.

```elixir
iex> Throttle.state
%{
  apply: %{},
  call: %{
    "say_hey" => %DebounceAndThrottle.Throttle{
      extra_data: %{fun: 'Function<45.65746770/0 in :erl_eval.expr/5>'},
      status: :executed,
      throttled_count: 0,
      throttled_until: -576460730743,
      throttled_until_utc: ~U[2022-03-12 22:47:39.829107Z]
    }
    ...
  },
  send: %{}
}
```

## Notes

### Choice of key

Your choice of what to use as the `key` depends on the granularity you want to throttle or debounce things.

So, bad example - if you wanted to throttle a `send_sms` function - if you used `send_sms` as the key it would throttle the function for every user in the system which is probably not what you want. So you might use something like `sms-#{user_id}` as the key to throttle it per user.

## License

This project is licensed under the MIT License - see the LICENSE.md file for details