Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to DispatcherQueueTimer.Debounce extension #569

Merged
merged 11 commits into from
Nov 26, 2024

Conversation

michael-hawker
Copy link
Member

@michael-hawker michael-hawker commented Nov 26, 2024

Fixes #143 (among other things not filed)

This PR does a lot of clean-up for the Debounce function, it adds:

  • More tests for a variety of scenarios for using Debounce
  • Adds Samples for Keyboard and Mouse debouncing using these APIs and improvements
  • Fixes the issue when switching the same timer from trailing to leading mode where the requested action would not fire immediately.
  • Switch to using ConditionalWeakTable instead of ConcurrentDictionary to ensure we don't capture the DispatcherQueueTimer
  • Fixes DispatcherQueueTimerExtensions.Debounce does not work with leading edge action #143 the issue of immediate not working well by setting IsRepeating on the timer to false during initialization. FYI @huynhsontung - This shouldn't effect your own registrations to the Tick method, if you're using Debounce, there should only be the one timer per Debounce, and your Tick should be called whenever the Debounce fires.

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Refactoring (no functional changes, no api changes)

What is the current behavior?

Switching from trailing to leading edge of Debounce wouldn't fire immediately.

Using Debounce would hold reference to DispatcherQueueTimer object.

What is the new behavior?

Switching from trailing to leading edge now fires immediately.

Using Debounce doesn't hold a reference to the DispatcherQueuTimer object

PR Checklist

Please check if your PR fulfills the following requirements:

  • Created a feature/dev branch in your fork (vs. submitting directly from a commit on main)
  • Based off latest main branch of toolkit
  • Tested code with current supported SDKs
  • New component
    • Documentation has been added
    • Sample in sample app has been added
    • Analyzers are passing for documentation and samples
    • Icon has been created (if new sample) following the Thumbnail Style Guide and templates
  • Tests for the changes have been added (if applicable)
  • Header has been added to all new source files
  • Contains NO breaking changes

Other information

  • Add test(s) based on DispatcherQueueTimerExtensions.Debounce does not work with leading edge action #143 for having custom Tick handler as well in user code.
  • Document the need for Debounces to have dedicated timers, as well as the behavior of setting IsRepeating to false on the timer.
    • It was never intended to support any sort of repeating ticked event in combination with the debouncer, that may be possible, but is outside of the scope of what we intend to do here. In the future, I could see a "repeating" parameter added which controls that in the future maybe - if we define the expected behavior, but for the default intended behavior now IsRepeating should be and remain false.

TODO: Still have the opposite scenario of Trailing to leading which is broken to fix.
…Table

This prevents capture holding onto the timer for garbage collection, validated with a unit test which fails with the ConcurrentDictionary, but succeeds with the ConditionalWeakTable
Because of the new Trailing/Leading behavior, we need the table in order to know if something was scheduled, otherwise we'd need reflection if we only stored the event handler, which wouldn't be AOT compatible.
…event of the DispatcherQueueTimer when using Debounce

Behavior should be well defined now.

In the future, we could define a 'repeating' behavior, if we think it'd be useful (not sure of the specific scenario), but to do so, I would recommend we encorporate it at the end of the current signature and make false by default:

public static void Debounce(this DispatcherQueueTimer timer, Action action, TimeSpan interval, bool immediate = false, bool repeat = false)

I would imagine, this would do something like continually pulse the Action/Tick event but when additional requests are received that it would disrupt that periodic pattern somehow based on the debounce configuration (trailing/leading)?
@michael-hawker michael-hawker marked this pull request as ready for review November 26, 2024 19:47
@michael-hawker
Copy link
Member Author

Alright, I've added more info to the docs, and added custom Tick handler to the tests to define/clarify their behavior as mentioned in the original issue filed in #143.

We've fixed that by explicitly setting IsRepeating to false. It was never intended to support it being true. So that'll have to be a future feature request if someone can advocate for a scenario where that's useful to have the event continue past the debouncing input signals?

From 26ac1f8:

In the future, we could define a 'repeating' behavior, if we think it'd be useful (not sure of the specific scenario), but to do so, I would recommend we encorporate it at the end of the current signature and make false by default:

public static void Debounce(this DispatcherQueueTimer timer, Action action, TimeSpan interval, bool immediate = false, bool repeat = false)

I would imagine, this would do something like continually pulse the Action/Tick event but when additional requests are received that it would disrupt that periodic pattern somehow based on the debounce configuration (trailing/leading)?

I'm not exactly sure how leading/trailing would effect/interrupt the timer in that fashion though... so future problem. Intent here of this PR was to make the timer work as well as intended and support the ability to dynamically change the leading/trailing edge, as well as provide better docs, samples, and clarification of its behavior and ensure we have proper test.

This PR can hopefully be a good guide for future improvements anyone would want to make in other areas of the Toolkit in bolstering existing API surfaces.

@Arlodotexe Arlodotexe self-requested a review November 26, 2024 19:53
Copy link
Member

@Arlodotexe Arlodotexe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great updates, especially the tests! A few minor change suggestions, otherwise, LGTM.

@michael-hawker
Copy link
Member Author

Thanks @Arlodotexe! Added those in, and we'll 🤞 on tests again... Oh, just thinking, we unblocked our CI by pinning on .NET 8, so could it be the switch to .NET 9 where we saw improvements? Or did we not get a clean build there... just wondering if there was some configuration change in between where you were seeing more stability?

@michael-hawker michael-hawker enabled auto-merge (squash) November 26, 2024 20:23
@Arlodotexe
Copy link
Member

Arlodotexe commented Nov 26, 2024

Thanks @Arlodotexe! Added those in, and we'll 🤞 on tests again... Oh, just thinking, we unblocked our CI by pinning on .NET 8, so could it be the switch to .NET 9 where we saw improvements? Or did we not get a clean build there... just wondering if there was some configuration change in between where you were seeing more stability?

@michael-hawker We started seeing consistent passing results in #493 after merging main into tests/diagnostics via 2120f62 and b588832, which were largely AoT fixes. We also updated our net8.0 version from 8.0.201 to 8.0.403, made webview2 a private asset dependency, and updated win2d.

We hadn't updated to net9 there. it's not clear why the merged changes fixed the issue, or why it's started happening again. MSBuilld was recently updated in CI, that might be it? We tried pinning the msbuild version long ago like we recently did for dotnet, but it's not working anymore and we're using the latest image.

@michael-hawker michael-hawker merged commit e3cd8ec into main Nov 26, 2024
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

DispatcherQueueTimerExtensions.Debounce does not work with leading edge action
2 participants