<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
{font-family:Verdana;
panose-1:2 11 6 4 3 5 4 4 2 4;}
@font-face
{font-family:"Times New Roman \(Body CS\)";
panose-1:2 11 6 4 2 2 2 2 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
font-size:10.0pt;
font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:#0563C1;
text-decoration:underline;}
span.EmailStyle18
{mso-style-type:personal-reply;
font-family:"Courier New";
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-size:10.0pt;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
--></style>
</head>
<body lang="EN-US" link="#0563C1" vlink="#954F72" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">My two cents: I think the functionality of debouncing should better be solved by a separate facility, rather than added to observables. An example would be a use case when multiple
observables trigger an expensive or delayed computation or a UI update.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">Something along the lines of<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><a href="https://github.com/TomasMikula/ReactFX/blob/master/reactfx/src/main/java/org/reactfx/util/FxTimer.java">https://github.com/TomasMikula/ReactFX/blob/master/reactfx/src/main/java/org/reactfx/util/FxTimer.java</a><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">or <o:p>
</o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><a href="https://github.com/andy-goryachev/FxEditor/blob/master/src/goryachev/fx/FxTimer.java">https://github.com/andy-goryachev/FxEditor/blob/master/src/goryachev/fx/FxTimer.java</a><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">cheers,<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">-andy<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><o:p> </o:p></span></p>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal" style="margin-bottom:12.0pt"><b><span style="font-size:12.0pt;color:black">From:
</span></b><span style="font-size:12.0pt;color:black">openjfx-dev <openjfx-dev-retn@openjdk.org> on behalf of Marius Hanl <mariushanl@web.de><br>
<b>Date: </b>Thursday, March 30, 2023 at 15:20<br>
<b>To: </b>John Hendrikx <john.hendrikx@gmail.com><br>
<b>Cc: </b>openjfx-dev@openjdk.org <openjfx-dev@openjdk.org><br>
<b>Subject: </b>Aw: Gauging interest in bindings that can delay changing their value (debounce/throttle)<o:p></o:p></span></p>
</div>
<div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">+ 1 for this. Debouncing is a common functionality for observables.<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">One of the common scenarios is obviously something like a search filter functionality, where typing in characters triggers an expensive calculation.<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">Debouncing solves the problem by doing that when nothing happened for some time, which is typically met when the user finished typing.<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif"> <o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">-- Marius<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">
<o:p></o:p></span></p>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">
<o:p></o:p></span></p>
<div style="border:none;border-left:solid #C3D9E5 1.5pt;padding:0in 0in 0in 8.0pt;margin-left:7.5pt;margin-top:7.5pt;margin-right:3.75pt;margin-bottom:3.75pt;-webkit-nbsp-mode: space;-webkit-line-break: after-white-space" name="quote">
<div style="margin-bottom:7.5pt">
<p class="MsoNormal"><b><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">Gesendet:</span></b><span style="font-size:9.0pt;font-family:"Verdana",sans-serif"> Donnerstag, 23. März 2023 um 18:09 Uhr<br>
<b>Von:</b> "John Hendrikx" <john.hendrikx@gmail.com><br>
<b>An:</b> openjfx-dev@openjdk.org<br>
<b>Betreff:</b> Gauging interest in bindings that can delay changing their value (debounce/throttle)<o:p></o:p></span></p>
</div>
<div name="quoted-content">
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">Hi list,<br>
<br>
I've been working on a potential new API (and proof of concept<br>
implementation) for adding a new type of fluent binding which can delay<br>
changing their values, and I'm wondering how much interest there is in<br>
such a thing.<br>
<br>
The main purpose of such an API is to prevent being flooded with changes<br>
when properties change often, or to simply delay certain actions until<br>
the user has settled on a selection or has stopped typing.<br>
<br>
For this purpose I would like to introduce a default method on<br>
`ObservableValue` with the signature:<br>
<br>
ObservableValue<T> throttle(Throttler throttler);<br>
<br>
The parameter `Throttler` can be obtained via static methods of a helper<br>
class named `FXThrottlers`. These provide various pre-configured<br>
throttlers that work correctly with JavaFX's event thread model. My<br>
current proof of concept provides:<br>
<br>
public static Throttler debounce(Duration quietPeriod);<br>
public static Throttler debounceTrailing(Duration quietPeriod);<br>
public static Throttler throttle(Duration period);<br>
public static Throttler throttleTrailing(Duration period);<br>
<br>
These are variations of similar concepts, and vary mostly in when<br>
exactly they will allow value changes; debouncers will wait for a period<br>
without any changes, while throttlers will periodically allow changes.<br>
The trailing variants will not immediately emit the first change but<br>
will wait for the period to elapse first; all variants will eventually<br>
take on the value of the source observable. Debouncing is typically<br>
used when you wish for an input to settle before taking action (like<br>
typing in a search bar), while throttling is used to give regular<br>
feedback but avoid doing so too often (like feedback during window<br>
resizing).<br>
<br>
Usage example which updates a preview panel when the user has finished<br>
(cursor) scrolling through a list view:<br>
<br>
ObjectProperty<T> selectedItem =<br>
listView.getSelectionModel().selectedItemProperty();<br>
<br>
selectedItem<br>
.throttle(FXThrottlers.debounceTrailing(Duration.ofMillis(500)))<br>
.addListener((obs, old, current) -> {<br>
if (current != null) {<br>
updatePreviewPanel(current);<br>
}<br>
});<br>
<br>
Implementation details:<br>
<br>
ObservableValue is part of javafx.base, and as such can't use animations<br>
or call Platform::runLater. The ThrottledBinding implementation has<br>
abstracted all of these out into the Throttler class, and FXThrottlers<br>
(which would live in javafx.graphics) therefore provides the necessary<br>
call backs to integrate property changes correctly back onto the JavaFX<br>
event thread. The Throttler class also simplifies testing; the test can<br>
provide its own timing source and background scheduler. The Throttler<br>
interface has the following methods:<br>
<br>
/**<br>
* Schedules a command to run on an unspecified thread after the time<br>
* given by {@code nanos} elapses.<br>
*<br>
* @param command a command to run, cannot be {@code null}<br>
* @param nanos a time in nanoseconds<br>
*/<br>
void schedule(Runnable command, long nanos);<br>
<br>
/**<br>
* Provides the current time in nanoseconds.<br>
*<br>
* @return the current time in nanoseconds<br>
*/<br>
long nanoTime();<br>
<br>
/**<br>
* Runs the given command as soon as possible on a thread specified<br>
by this<br>
* throttler for updating property values.<br>
*<br>
* @param command a command to run, cannot be {@code null}<br>
*/<br>
void update(Runnable command);<br>
<br>
/**<br>
* Given the current elapsed time in the current change window, and the<br>
* amount of time elapsed since the last change was detected,<br>
determines<br>
* if and by how much the current change window should be extended.<br>
*<br>
* @param elapsed nanoseconds elapsed since the start of the<br>
current change window<br>
* @param elapsedSinceLastChange nanoseconds elapsed since the last<br>
change<br>
* @return nanoseconds to extend the window with<br>
*/<br>
long determineInterval(long elapsed, long elapsedSinceLastChange);<br>
<br>
For testing purposes, the schedule and nanoTime can be provided such<br>
that the throttle function can be tested deterministically. For<br>
integrating with JavaFX, update is implemented as<br>
`Platform.runLater(command)`. The schedule and nanoTime methods<br>
delegate to an Executor and System.nanoTime respectively. When using<br>
properties without JavaFX, Throttler implementations can be provided<br>
which run property updates on a scheduler thread (just calling<br>
Runnable::run on the current thread) or via some user provided executor.<br>
<br>
A sample test case looks like this (read with a mono space font :-)):<br>
<br>
@Test<br>
void testThrottleLeadingAndTrailing() {<br>
// create Throttler with deterministic behavior:<br>
Throttler throttler =<br>
create(Throttler.IntervalHandler.throttle(Duration.ofNanos(4));<br>
<br>
// create throttled observable:<br>
ObservableValue<String> binding = source.throttle(throttler);<br>
<br>
assertChanges(<br>
binding,<br>
"--a-b--c---d-----e-------f-g-----------f-g-----",<br>
"--a---b---c---d---e------f---g---------f---g---"<br>
);<br>
<br>
assertInvalidations(<br>
binding,<br>
"--a-b--c---d-----e-------f-g-----------f-g-----",<br>
"--i---i---i---i---i------i---i---------i---i---"<br>
);<br>
}<br>
<br>
Thanks for reading, I look forward to your feedback!<br>
<br>
--John<br>
<br>
<br>
<br>
<o:p></o:p></span></p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>