<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hi Andy,<br>
    </p>
    <div class="moz-cite-prefix">On 03/04/2023 21:14, Andy Goryachev
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:DM5PR1001MB21725E63DF6A5DD21B47CAE3E5929@DM5PR1001MB2172.namprd10.prod.outlook.com">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta name="Generator" content="Microsoft Word 15 (filtered
        medium)">
      <style>@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;}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;}div.WordSection1
        {page:WordSection1;}</style>
      <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.</span></p>
      </div>
    </blockquote>
    <p>Can you elaborate on what cases you see with multiple
      observables?  I think you mean events here; events can also
      benefit from debouncing or throttling, but that's something quite
      different.  An ObservableValue is not an event source, but the
      values it takes on can be mapped, delayed or even interpolated. 
      In the end however, there is always a value immediately available,
      making them suitable for binding.  An event source on the other
      hand can't supply events (values) on demand, can't repeat them and
      it does not remember the last one -- you can't bind an event
      source in the same way either; at the most you can set a default
      value and then update it when events come in.<br>
    </p>
    <br>
    <blockquote type="cite"
cite="mid:DM5PR1001MB21725E63DF6A5DD21B47CAE3E5929@DM5PR1001MB2172.namprd10.prod.outlook.com">
      <div class="WordSection1">
        <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.</span></p>
      </div>
    </blockquote>
    <p>Could you clarify at what point an API is too large or too
      complicated? Certainly there are far larger API's in the Java
      ecosystem and in the JDK itself.  I'm pretty sure there are far
      more complicated ones as well.  <br>
    </p>
    <p>--John<br>
    </p>
    <br>
    <blockquote type="cite"
cite="mid:DM5PR1001MB21725E63DF6A5DD21B47CAE3E5929@DM5PR1001MB2172.namprd10.prod.outlook.com">
      <div class="WordSection1">
        <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"">-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 <a class="moz-txt-link-rfc2396E" href="mailto:john.hendrikx@gmail.com"><john.hendrikx@gmail.com></a><br>
              <b>Date: </b>Monday, April 3, 2023 at 12:05<br>
              <b>To: </b>Andy Goryachev
              <a class="moz-txt-link-rfc2396E" href="mailto:andy.goryachev@oracle.com"><andy.goryachev@oracle.com></a>, Marius Hanl
              <a class="moz-txt-link-rfc2396E" href="mailto:mariushanl@web.de"><mariushanl@web.de></a><br>
              <b>Cc: </b><a class="moz-txt-link-abbreviated" href="mailto:openjfx-dev@openjdk.org">openjfx-dev@openjdk.org</a>
              <a class="moz-txt-link-rfc2396E" href="mailto:openjfx-dev@openjdk.org"><openjfx-dev@openjdk.org></a><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$"
            moz-do-not-send="true">
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$"
                moz-do-not-send="true">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$"
                moz-do-not-send="true">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"
                  moz-do-not-send="true">
                  <openjfx-dev-retn@openjdk.org></a> on behalf of
                Marius Hanl <a href="mailto:mariushanl@web.de"
                  moz-do-not-send="true">
                  <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"
                  moz-do-not-send="true"><john.hendrikx@gmail.com></a><br>
                <b>Cc: </b><a href="mailto:openjfx-dev@openjdk.org"
                  moz-do-not-send="true" class="moz-txt-link-freetext">openjfx-dev@openjdk.org</a>
                <a href="mailto:openjfx-dev@openjdk.org"
                  moz-do-not-send="true">
                  <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"
                          moz-do-not-send="true"><john.hendrikx@gmail.com></a><br>
                        <b>An:</b> <a
                          href="mailto:openjfx-dev@openjdk.org"
                          moz-do-not-send="true"
                          class="moz-txt-link-freetext">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>
    </blockquote>
  </body>
</html>