{"id":30196260,"url":"https://github.com/soenneker/soenneker.utils.debounce","last_synced_at":"2026-07-16T14:01:17.810Z","repository":{"id":307691226,"uuid":"1030374854","full_name":"soenneker/soenneker.utils.debounce","owner":"soenneker","description":"Delays execution of an async action until a specified interval has passed without new invocations. Ideal for throttling high-frequency operations like UI events, logging, or API calls.","archived":false,"fork":false,"pushed_at":"2026-07-10T01:57:05.000Z","size":356,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-10T03:38:22.530Z","etag":null,"topics":["csharp","debounce","debouncer","dotnet","util","utility","utils"],"latest_commit_sha":null,"homepage":"https://soenneker.com","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soenneker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"soenneker","thanks_dev":"soenneker"}},"created_at":"2025-08-01T14:28:11.000Z","updated_at":"2026-07-10T01:57:08.000Z","dependencies_parsed_at":"2026-06-14T23:02:33.678Z","dependency_job_id":null,"html_url":"https://github.com/soenneker/soenneker.utils.debounce","commit_stats":null,"previous_names":["soenneker/soenneker.utils.debounce"],"tags_count":50,"template":false,"template_full_name":null,"purl":"pkg:github/soenneker/soenneker.utils.debounce","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.utils.debounce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.utils.debounce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.utils.debounce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.utils.debounce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soenneker","download_url":"https://codeload.github.com/soenneker/soenneker.utils.debounce/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.utils.debounce/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35437768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-13T02:00:06.543Z","response_time":119,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","debounce","debouncer","dotnet","util","utility","utils"],"created_at":"2025-08-13T05:17:22.586Z","updated_at":"2026-07-16T14:01:17.784Z","avatar_url":"https://github.com/soenneker.png","language":"C#","funding_links":["https://github.com/sponsors/soenneker","https://thanks.dev/soenneker"],"categories":[],"sub_categories":[],"readme":"[![](https://img.shields.io/nuget/v/soenneker.utils.debounce.svg?style=for-the-badge)](https://www.nuget.org/packages/soenneker.utils.debounce/)\n[![](https://img.shields.io/github/actions/workflow/status/soenneker/soenneker.utils.debounce/publish-package.yml?style=for-the-badge)](https://github.com/soenneker/soenneker.utils.debounce/actions/workflows/publish-package.yml)\n[![](https://img.shields.io/nuget/dt/soenneker.utils.debounce.svg?style=for-the-badge)](https://www.nuget.org/packages/soenneker.utils.debounce/)\n[![](https://img.shields.io/github/actions/workflow/status/soenneker/soenneker.utils.debounce/codeql.yml?label=CodeQL\u0026style=for-the-badge)](https://github.com/soenneker/soenneker.utils.debounce/actions/workflows/codeql.yml)\n\n# ![](https://user-images.githubusercontent.com/4441470/224455560-91ed3ee7-f510-4041-a8d2-3fc093025112.png) Soenneker.Utils.Debounce\n\nA utility that lets you *debounce* work in .NET.\nGive it a delay, async/sync delegate, and the `Debouncer` guarantees that multiple rapid calls collapse into exactly one invocation.\n\n---\n\n### Why would I need this?\n\n* **API calls:** Prevent hammering a server while the user types.\n* **Disk I/O:** Batch frequent save requests into a single write.\n* **Telemetry:** Send aggregated metrics after bursts of activity.\n* **Search boxes / auto-complete:** React only after the user pauses typing.\n\n---\n\n### Quick start\n\n```bash\ndotnet add package Soenneker.Utils.Debounce\n```\n\n```csharp\nusing Soenneker.Utils.Debounce;\n\nvar debouncer = new Debouncer();\n\n// Fire only once, 300 ms after the *last* request:\nvoid OnTextChanged(string text)\n{\n    debouncer.Debounce(\n        delayMs: 300,\n        action: async ct =\u003e\n        {\n            var results = await SearchAsync(text, ct);\n            UpdateUI(results);\n        });\n}\n\nvoid OnResize()\n{\n    debouncer.Debounce(\n        delayMs: 250,\n        action: () =\u003e\n        {\n            // Runs on the thread-pool after 250 ms of quiescence\n            SaveWindowLayout();\n        });\n}\n```\n\n#### Leading-edge execution\n\nPass `runLeading: true` if you want the first call to run immediately **and** the trailing call to run after the quiet period:\n\n```csharp\ndebouncer.Debounce(\n    delayMs: 500,\n    runLeading: true,\n    action: ct =\u003e Logger.LogAsync(\"Burst started\", ct));\n```\n\nEither wrap it in a `using` statement or dispose the debouncer when you�re done:\n\n```csharp\nawait debouncer.DisposeAsync();\n```\n\n`DisposeAsync()` waits for any in-flight work to finish, ensuring graceful shutdown.\n\n---\n\n### Design highlights\n\n* **Pure TPL:** Built on `System.Threading.Timer`\n* **Thread-safe:** Internal state is guarded with `Interlocked` swaps.\n* **Cancellation-friendly:** Each queued delegate receives *its own* `CancellationToken`.\n* **Zero allocations on idle:** Work objects are created only when you call `Debounce`.\n* **Tested:** xUnit suite covering timing, cancellation, and disposal semantics.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoenneker%2Fsoenneker.utils.debounce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoenneker%2Fsoenneker.utils.debounce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoenneker%2Fsoenneker.utils.debounce/lists"}