<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:"Yu Gothic";
panose-1:2 11 4 0 0 0 0 0 0 0;}
@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;}
@font-face
{font-family:"\@Yu Gothic";
panose-1:2 11 4 0 0 0 0 0 0 0;}
/* 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.EmailStyle20
{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="purple" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">Right. I am not saying we should take these classes as is.<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"">In my opinion, this functionality might be better supported by a separate facility(ies). Specifically, to handle the case of multiple observables.<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"">I also think the APIs are large and complicated enough already, it might be better to add/extract (a rarely used) functionality to a separate class or set of classes.<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"">-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>
<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">John Hendrikx <john.hendrikx@gmail.com><br>
<b>Date: </b>Monday, April 3, 2023 at 12:05<br>
<b>To: </b>Andy Goryachev <andy.goryachev@oracle.com>, Marius Hanl <mariushanl@web.de><br>
<b>Cc: </b>openjfx-dev@openjdk.org <openjfx-dev@openjdk.org><br>
<b>Subject: </b>[External] : Re: Gauging interest in bindings that can delay changing their value (debounce/throttle)<o:p></o:p></span></p>
</div>
<p>Hi Andy,<o:p></o:p></p>
<p>Those examples seem to be just timers, it would be hard to construct the primitives like throttle and debounce with these, as they don't take into account when the value last changed, or whether or not is important that the value changed again (reset timer
or not). These timers would just run forever, while the functionality I propose here would have no timers running when things are stable. The timeout would also trigger precisily on the first value change. Having a running timer is more like sampling, not
throttling or debouncing.<o:p></o:p></p>
<p>The functionality I'm proposing would be more along the lines of #reduceSucessions in
<a href="https://urldefense.com/v3/__https:/github.com/TomasMikula/ReactFX/blob/master/reactfx/src/main/java/org/reactfx/EventStream.java__;!!ACWV5N9M2RV99hQ!IUX7QjZNn8vhK8ISBY_DO3YOqEeBlcegZahboYpjq2XsYz5Xj-XoFVxWKltMxZxJS2aFla51yf2gREfkFoeF1VL_66lB$">
https://github.com/TomasMikula/ReactFX/blob/master/reactfx/src/main/java/org/reactfx/EventStream.java</a> -- except that it would never support accumulation or combining of values (that's something for streams, not for values).<o:p></o:p></p>
<p>--John<o:p></o:p></p>
<div>
<p class="MsoNormal"><span style="font-size:11.0pt">On 03/04/2023 18:47, Andy Goryachev wrote:<o:p></o:p></span></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<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.</span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""> </span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">Something along the lines of</span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><a href="https://urldefense.com/v3/__https:/github.com/TomasMikula/ReactFX/blob/master/reactfx/src/main/java/org/reactfx/util/FxTimer.java__;!!ACWV5N9M2RV99hQ!IUX7QjZNn8vhK8ISBY_DO3YOqEeBlcegZahboYpjq2XsYz5Xj-XoFVxWKltMxZxJS2aFla51yf2gREfkFoeF1YeFiEIP$">https://github.com/TomasMikula/ReactFX/blob/master/reactfx/src/main/java/org/reactfx/util/FxTimer.java</a></span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">or </span>
<o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""><a href="https://urldefense.com/v3/__https:/github.com/andy-goryachev/FxEditor/blob/master/src/goryachev/fx/FxTimer.java__;!!ACWV5N9M2RV99hQ!IUX7QjZNn8vhK8ISBY_DO3YOqEeBlcegZahboYpjq2XsYz5Xj-XoFVxWKltMxZxJS2aFla51yf2gREfkFoeF1VyqfNCE$">https://github.com/andy-goryachev/FxEditor/blob/master/src/goryachev/fx/FxTimer.java</a></span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""> </span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">cheers,</span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New"">-andy</span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""> </span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""> </span><o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Courier New""> </span><o:p></o:p></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 <a href="mailto:openjfx-dev-retn@openjdk.org">
<openjfx-dev-retn@openjdk.org></a> on behalf of Marius Hanl <a href="mailto:mariushanl@web.de">
<mariushanl@web.de></a><br>
<b>Date: </b>Thursday, March 30, 2023 at 15:20<br>
<b>To: </b>John Hendrikx <a href="mailto:john.hendrikx@gmail.com"><john.hendrikx@gmail.com></a><br>
<b>Cc: </b><a href="mailto:openjfx-dev@openjdk.org">openjfx-dev@openjdk.org</a> <a href="mailto:openjfx-dev@openjdk.org">
<openjfx-dev@openjdk.org></a><br>
<b>Subject: </b>Aw: Gauging interest in bindings that can delay changing their value (debounce/throttle)</span><o:p></o:p></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.</span><o:p></o:p></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.</span><o:p></o:p></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.</span><o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif"> </span><o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">-- Marius</span><o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">
</span><o:p></o:p></p>
<div>
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Verdana",sans-serif">
</span><o:p></o:p></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" <a href="mailto:john.hendrikx@gmail.com"><john.hendrikx@gmail.com></a><br>
<b>An:</b> <a href="mailto:openjfx-dev@openjdk.org">openjfx-dev@openjdk.org</a><br>
<b>Betreff:</b> Gauging interest in bindings that can delay changing their value (debounce/throttle)</span><o:p></o:p></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>
</span><o:p></o:p></p>
</div>
</div>
</div>
</div>
</div>
</blockquote>
</div>
</body>
</html>