Debounce.hs
-- | Take incoming Aff requests, and perform them sequentially.
-- | In case a request is received while another is being performed,
-- | queue the new request instead. The queued request will be performed once the
-- | one before finishes. The request queue contains at most a single request;
-- | in case multiple ones are added, only the newest one stays.
-- |
-- | This ensures optimal semantics in the following sense: At every point in time,
-- | the freshest possible result is returned to the caller.
module Frontend.Component.Debounce where
import Prelude
import Data.Maybe (Maybe(..))
import Data.Newtype (class Newtype)
import Effect.Aff (Aff)
import Effect.Aff.Class (class MonadAff, liftAff)
import Halogen as H
import Halogen.HTML as HH
newtype Output t = Output t
derive instance newtypeOutput :: Newtype (Output t) _
data Query t a = Query (Aff t) a
type State t = { seeking :: Boolean, seekQueue :: Maybe (Aff t) }
debounce :: forall input t m. MonadAff m => H.Component HH.HTML (Query t) input (Output t) m
debounce =
H.mkComponent
{ initialState: \_ -> { seeking: false, seekQueue: Nothing }
, render: const $ HH.text ""
, eval: H.mkEval $ H.defaultEval { handleQuery = handleQuery }
}
where
handleQuery :: forall action a. Query t a -> H.HalogenM (State t) action () (Output t) m (Maybe a)
handleQuery = case _ of
Query computation a -> do
H.modify_ _ { seekQueue = Just computation }
seeking <- H.gets _.seeking
case seeking of
true -> pure (Just a)
false -> do
H.modify_ _ { seeking = true, seekQueue=Nothing }
res <- liftAff computation
H.modify_ _ { seeking = false }
H.raise $ Output res
q <- H.gets _.seekQueue
case q of
Nothing -> pure (Just a)
Just v -> handleQuery (Query v a)