From e7c2d58147f5611597c22f0c2588309ed8d154a5 Mon Sep 17 00:00:00 2001 From: Holger Schmidt Date: Sat, 2 Mar 2024 00:21:53 +0100 Subject: [PATCH] documented and commented debounce method to prevent misuse --- src/Fabulous/Cmd.fs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Fabulous/Cmd.fs b/src/Fabulous/Cmd.fs index a394051d6..eb5571ade 100644 --- a/src/Fabulous/Cmd.fs +++ b/src/Fabulous/Cmd.fs @@ -188,20 +188,33 @@ module Cmd = let inline msgOption (task: Task<'msg option>) = OfAsync.msgOption(task |> Async.AwaitTask) - /// Command to issue a message if no other message has been issued within the specified timeout + /// Creates a factory for Commands that dispatch a message only + /// if the factory produces no other Command within the specified timeout. + /// Helps control how often a message is dispatched by delaying the dispatch after a period of inactivity. + /// Useful for handling noisy inputs like keypresses or scrolling, and preventing too many actions in a short time, like rapid button clicks. + /// Note that this creates an object with internal state and is intended to be used per Program or longer-running background process + /// rather than once per message in the update function. + /// The time to wait for the next Command from the factory in milliseconds. + /// Maps a factory input value to a message for delayed dispatch. + /// A Command factory function that maps an input value to a "sleeper" Command which dispatches a delayed message (mapped from the value). + /// This command is cancelled if the factory produces another Command within the specified timeout; otherwise it succeeds and the message is dispatched. let debounce (timeout: int) (fn: 'value -> 'msg) : 'value -> Cmd<'msg> = - let funLock = obj() - let mutable cts: CancellationTokenSource = null + let funLock = obj() // ensures safe access to resources shared across different threads + let mutable cts: CancellationTokenSource = null // if set, allows cancelling the last issued Command + // return a factory function mapping input values to "sleeper" Commands with delayed dispatch fun (value: 'value) -> [ fun dispatch -> lock funLock (fun () -> + // cancel the last sleeping Command issued earlier from this factory if cts <> null then cts.Cancel() cts.Dispose() + // make cancellation available to the factory's next Command cts <- new CancellationTokenSource() + // asynchronously wait for the specified time before dispatch Async.Start( async { do! Async.Sleep(timeout) @@ -209,6 +222,7 @@ module Cmd = lock funLock (fun () -> dispatch(fn value) + // done; invalidate own cancellation token if cts <> null then cts.Dispose() cts <- null)