{"id":22751836,"url":"https://github.com/boraseoksoon/throttler","last_synced_at":"2025-04-15T19:32:26.405Z","repository":{"id":37321423,"uuid":"340789749","full_name":"boraseoksoon/Throttler","owner":"boraseoksoon","description":"One Line to throttle, debounce and delay: Say Goodbye to Reactive Programming such as RxSwift and Combine.","archived":false,"fork":false,"pushed_at":"2024-06-21T21:08:42.000Z","size":125,"stargazers_count":160,"open_issues_count":5,"forks_count":23,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-25T16:46:13.982Z","etag":null,"topics":["async","asynchronous-programming","cocoa","combine","dispatchqueue","dispatchworkitem","foundation","function","input","ios","macos","swift","swift-package-manager","swiftui","throttle","throttle-requests","throttler","uikit","validation","validation-library"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/boraseoksoon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-21T01:11:08.000Z","updated_at":"2025-03-13T13:59:23.000Z","dependencies_parsed_at":"2024-06-22T12:51:43.016Z","dependency_job_id":null,"html_url":"https://github.com/boraseoksoon/Throttler","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boraseoksoon%2FThrottler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boraseoksoon%2FThrottler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boraseoksoon%2FThrottler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boraseoksoon%2FThrottler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boraseoksoon","download_url":"https://codeload.github.com/boraseoksoon/Throttler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249138861,"owners_count":21218962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["async","asynchronous-programming","cocoa","combine","dispatchqueue","dispatchworkitem","foundation","function","input","ios","macos","swift","swift-package-manager","swiftui","throttle","throttle-requests","throttler","uikit","validation","validation-library"],"created_at":"2024-12-11T05:07:37.371Z","updated_at":"2025-04-15T19:32:26.384Z","avatar_url":"https://github.com/boraseoksoon.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://cdn.icon-icons.com/icons2/390/PNG/512/grim-reaper_38285.png\" alt=\"Throttler\" title=\"Throttler\" width=\"537\"/\u003e\n\u003c/p\u003e\n\n\u003csub\u003eIcon credits: [Lorc, Delapouite \u0026 contributors](https://game-icons.net)\u003c/sub\u003e\n\u003cbr\u003e\n\u003cbr\u003e\n\n# Throttler\n\nDrop one line to use throttle, debounce, and delay with full thread safety: say goodbye to reactive programming like RxSwift and Combine.\n\n# At a glance\n\n```swift\n\nimport Throttler\n\ndebounce {\n    print(\"debounce 1 sec\")\n}\n\nthrottle {\n    print(\"throttle 1 sec\")\n}\n\ndelay {\n    print(\"delay 1 sec\")\n}\n\n```\n\n# 💥 Basic Usage in SwiftUI\n\nHere's how you can quickly get started.\n\n```swift\nimport SwiftUI\nimport Throttler\n\nstruct ContentView: View {\n    var body: some View {\n        VStack {\n            Button(action: {\n                for i in 1...10000000 {\n                    throttle {\n                        print(\"throttle: \\(i)\")\n                    }\n                }\n            }) {\n                Text(\"throttle\")\n            }\n            // Expected Output: Will print \"throttle : \\(i)\" every 1 second (by default)\n\n            Button(action: {\n                delay {\n                    print(\"delayed 2 seconds\")\n                }\n            }) {\n                Text(\"delay\")\n            }\n            // Expected Output: Will print \"Delayed 2 seconds\" after 2 seconds\n\n            Button(action: {\n                for i in 1...10000000 {\n                    debounce {\n                        print(\"debounce \\(i)\")\n                    }\n                }\n            }) {\n                Text(\"debounce\")\n            }\n            // Expected Output: Will print \"debounce\" only after the button has not been clicked for 1 second\n        }\n    }\n}\n```\n\n# 🌟 Features\n- **Throttle**: Limit how often an operation can be triggered over time. Thanks to Swift's actor model, this operation is thread-safe.\n- **Debounce**: Delay the execution of an operation until a certain time has passed without any more triggers. This operation is also thread-safe, courtesy of Swift's actor model.\n- **Delay**: Execute an operation after a certain amount of time. With Swift's actor model, you can rest assured that this operation is thread-safe too.\n\n# 🦾 Thread Safety\nAll of these operations are executed in a thread-safe manner, leveraging the Swift actor model.\nThis guarantees safe access and modification of shared mutable state within the closure of throttle, debounce, and delay functions, regardless of the number of threads involved.\n\nFeed any shared resource into them (debounce, throttle, debounce). The functions will handle everything out of box.\n\n```swift\nimport Foundation\n\n/* a simple thread safe test. */\n\nvar a = 0\n\nDispatchQueue.global().async {\n    for _ in Array(0...10000) {\n        throttle(.seconds(0.1), by: .ownedActor) {\n            a+=1\n            print(\"throttle1 : \\(a)\")\n        }\n    }\n}\n\nDispatchQueue.global().async {\n    for _ in Array(0...100) {\n        throttle(.seconds(0.01), by: .ownedActor) {\n            a+=1\n            print(\"throttle2 : \\(a)\")\n        }\n    }\n}\n\nDispatchQueue.global().async {\n    for _ in Array(0...100) {\n        throttle(.seconds(0.001), by: .ownedActor) {\n            a+=1\n            print(\"throttle3 : \\(a)\")\n        }\n    }\n}\n\nDispatchQueue.global().async {\n    for _ in Array(0...100) {\n        debounce(.seconds(0.001), by: .ownedActor) {\n            a+=1\n            print(\"debounce1 : \\(a)\")\n        }\n    }\n}\n\n//throttle3 : 1\n//throttle3 : 2\n//throttle3 : 3\n//throttle3 : 4\n//throttle3 : 5\n//throttle3 : 6\n//throttle3 : 7\n//throttle3 : 8\n//throttle3 : 9\n//throttle3 : 10\n//throttle3 : 11\n//debounce1 : 12\n//throttle3 : 13\n//throttle2 : 14\n//throttle1 : 15\n//throttle1 : 16\n//throttle1 : 17\n//throttle1 : 18\n//throttle1 : 19\n//throttle1 : 20\n//throttle1 : 21\n//throttle1 : 22\n//throttle1 : 23\n//throttle1 : 24\n//throttle1 : 25\n//throttle1 : 26\n//throttle1 : 27\n\n/// safe from race condition =\u003e ✅\n/// safe from data race      =\u003e ✅\n\n```\n\n# 🚀 Advanced Features for Throttler\n\nThrottler stands out not just for its advanced features, but also for its incredibly simple-to-use API. Here's how it gives you more, right out of the box, with just a **one-liner closure**:\n\n#### Throttle Options\n\n1. **default**: Standard throttling behavior without any fancy tricks. Simply include the throttle function with a one-liner closure, and you're good to go.\n2. **ensureLast**: Ensures the last call within the interval gets executed. Just a single line of code.\n\n#### Debounce Options\n\n1. **default**: Standard debounce behavior with just a one-liner closure. Include the debounce function, and it works like a charm.\n2. **runFirst**: Get instant feedback with the first call executed immediately, then debounce later. All of this with a simple one-liner.\n\n## DebounceOptions\n\n1. **default**: The standard debounce behavior by default.\n\n```swift\n/// by default: duration 1 sec and default debounce (not runFirst)\n\nfor i in Array(0...100) {\n    debounce {\n        print(\"debounce : \\(i)\")\n    }\n}\n\n// debounce : 100\n```\n\n2. **runFirst**: Executes the operation immediately, then debounces subsequent calls.\n\n```swift\n/// Expected Output: Executes a first task immediately, then debounce only after 1 second since the last operation.\n\nfor i in Array(0...100) {\n    debounce(.seconds(2), option: .runFirst) {\n        print(\"debounce : \\(i)\")\n    }\n}\n\n// debounce : 1        =\u003e 💥\n// debounce : 100\n```\n\n## ThrottleOptions\n\n#### Options Explained\n\n1. **default**: The standard throttle behavior.\n\n```swift\n\n/// Throttle and executes once every 1 second.\n\nfor i in 1...100000 {\n    throttle {\n        print(\"throttle: \\(i)\")\n    }\n}\n\n// throttle: 0\n// throttle: 41919\n// throttle: 86807\n\n```\n\n2. **ensureLast**: Guarantees that the last call within the interval will be executed.\n\n```swift\n/// Guarantees the last call no matter what even after a throttle duration and finished.\n\nfor i in 1...100000 {\n    throttle(option: .ensureLast) {\n        print(\"throttle : \\(i)\")\n    }\n}\n\n// throttle : 0 \n// throttle : 16363 \n// throttle : 52307\n// throttle : 74711\n// throttle : 95747\n// throttle : 100000    =\u003e 💥\n\n```\n\nThrottler makes it extremely simple and easy to use advanced features with just a one-liner, unlike RxSwift and Combine where custom implementations are often required.\n\n# Comparison with RxSwift and Combine for the advanced options in code\n\n- **RxSwift**:\n\n```swift\nimport RxSwift\n\nlet disposeBag = DisposeBag()\nObservable.from(1...100000)\n    .throttle(RxTimeInterval.milliseconds(500), latest: false, scheduler: MainScheduler.instance)\n    .subscribe(onNext: { i in\n        print(\"throttle : \\(i)\")\n    })\n    .disposed(by: disposeBag)\n```\n\n\n- **Combine**:\n\n#### Throttle options\n\n```swift\nimport Combine\n\nvar cancellables = Set\u003cAnyCancellable\u003e()\nPublishers.Sequence(sequence: 1...100000)\n    .throttle(for: .milliseconds(500), scheduler: DispatchQueue.main, latest: false)\n    .sink(receiveValue: { i in\n        print(\"throttle : \\(i)\")\n    })\n    .store(in: \u0026cancellables)\n```\n\n- **Throttler**:\n  \n```swift\nimport Throttler\n\nthrottle {\n    print(\"hi\")\n}\n```\n\n# Advantages over Combine and RxSwift's Throttle and Debounce\n- **Simplicity**: These functions are designed to be straightforward and easy to use. With just a single line of code, you can have them up and running.\n- **Out-of-the-Box Thread Safety**: Simply pass any shared resource into the debounce, throttle, or delay functions without any concerns. The implementation leverages the Swift actor model, providing robust protection against data races for mutable states. This ensures that you can safely access and modify shared mutable state without any worries about thread safety. It will handle everything in a thread-safe manner out of box.\n- **No Need for Reactive Programming**: If you prefer not to use reactive programming paradigms, this approach provides an excellent alternative. It allows you to enjoy the benefits of throttling and debouncing without having to adopt a reactive programming style.\n\n# :warning: Important Note on Identifiers parameters for debounce and throttle\n\n\u003e **Highly Recommended**: While the functions are intentionally designed to run out of the box without specifying an identifier in favor of brevity, it is **strongly recommended** to provide a custom identifier for `debounce` and `throttle` operations for better control and organization.\n\n### Example with Custom Identifier\n\n```swift\n\n// simple and come in handy by default\n\nthrottle {\n    print(\"simple\")\n}\n\n// recommended\n\nthrottle(identifier: \"custom_throttle_id\") {\n    print(\"This is a recommended way of using throttled.\")\n}\n\n// simple and come in handy by default\n\ndebounce {\n    print(\"simple\")\n}\n\n// recommended\n\ndebounce(.seconds(2), identifier: \"custom_debounce_id\") {\n    print(\"This is a recommended way of using debounced.\")\n}\n\n```\n\n# Throttler V2.0.0 - Actor-based Update\n\n## What's New in V2.0.0\n\nIn favor of new Swift concurrency, this release completely relies on and leverages the new actor model introduced in Swift 5.5 for better performance and safer code execution.\nThe previous versions that used standard Swift methods for task management such as GCD has been completely removed as deprecated to emphasize the use of the actor in a unified way.\n\n**(Please be aware that the minimum version requirement has been raised to iOS 16.0, macOS 13.0, watchOS 9.0, and tvOS 16.0.)**\n\n# Struct based (Deprecated)\n\nAs of V2.0.0, struct based way was removed as deprecated in favor of Swift actor type. \nPlease migrate to functions. (throttle, debounce and delay) \n\n# Requirements\n\niOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0\n\n# Installation\n\n## Swift Package Manager\n\nTo use the latest V2.0.9 version, add the package to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/YourRepo/Throttler.git\", .upToNextMajor(from: \"2.0.9\"))\n]\n```\n\nor in **Xcode**: \n- File \u003e Swift Packages \u003e Add Package Dependency\n- Add `https://github.com/boraseoksoon/Throttler.git`\n- Click Next.\n- Done.\n\n# Contact\n\nboraseoksoon@gmail.com\n\nPull requests are warmly welcome as well.\n\n# License\n\nThrottler is released under the MIT license. See LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboraseoksoon%2Fthrottler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboraseoksoon%2Fthrottler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboraseoksoon%2Fthrottler/lists"}