Ooh, you're talking about the perennial
I'm-a-test-runner-and-there-are-stray-timeout issue.
You're right, making the default implementation of the dispatcher settable
(tied to a deferred binding?) could make a lot of things better at test
time, couldn't it?

On Fri, Sep 4, 2009 at 12:37 PM, Ray Ryan <rj...@google.com> wrote:

> Because the dispatcher methods are not static you can write your code to
> have the dispatcher injected, and at test time provide whatever alternative
> implementation you want. So long as you don't use the static get method
> outside of your Gin module or whatever, you're golden.
> Not good enough?
>
>
> On Fri, Sep 4, 2009 at 12:20 PM, Brian Slesinsky <bslesin...@gmail.com>wrote:
>
>>
>> How do we test methods that schedule commands?
>>
>> Maybe there should be a way to test that a method schedules some
>> commands without actually executing them? From a testing point of
>> view, it would be nice to be able to push a context that captures all
>> commands, call the method under test, and then pop the context and
>> verify that it contains the commands you expect. (The API should be
>> optimized away if it's not used, which would usually be the case in
>> production.)
>>
>> Alternately, you might want to have a way to force commands scheduled
>> by the method under test to run before doing your assertions, so that
>> you can verify that the method under test had its intended effect,
>> regardless of whether it uses commands to accomplish it.  I think if
>> you can capture commands, then you could do this too. (It gets tricky
>> to do if commands can schedule other commands, so it's worth writing a
>> test utility, but probably doesn't require anything more than
>> capturing from the core API.)
>>
>> - Brian
>>
>> On Sep 3, 7:08 pm, Bruce Johnson <br...@google.com> wrote:
>> > Okay, here's a strawman for a new-and-improved proposal. All these would
>> be
>> > in core.
>> > // "Deferred command" = on the other side of the event loop
>> > interface DeferredCommands {
>> >   public static DeferredCommands get();
>> >
>> >   public void add(Command cmd);
>> >   public void add(Command cmd, boolean asap);  // asap = faster than
>> > setTimeout(0)
>> >   public void addPause();
>> >
>> > }
>> >
>> > // "Finally command" = before you end the current stack trace
>> > interface FinallyCommands {
>> >   public static FinallyCommands get();
>> >
>> >   public void add(Command cmd);
>> >
>> > }
>> >
>> > // "Incremental command" = call repeatedly quickly to avoid SSWs
>> > interface IncrementalCommands {
>> >   public static IncrementalCommands get();
>> >
>> >   public void add(Command cmd);
>> >   public void add(Command cmd, boolean asap);
>> >
>> > }
>> >
>> > // "Timed command" = call based clock time (aka regular old timers)
>> > interface TimedCommands {
>> >   public static TimedCommand get();
>> >
>> >   public TimerController scheduleOnce(Command cmd, int millis);
>> >   public TimerController scheduleRecurring(Command cmd, int millis);
>> >
>> > }
>> >
>> > // Allows optional control over a timer after it's created.
>> > // If the return values in scheduleOnce, etc. aren't used, extra code
>> can
>> > maybe optimize away.
>> > interface TimerController {
>> >   public void pause();
>> >   public void resume();
>> >   public void cancel();
>> >
>> > }
>> >
>> > I think that maybe consolidating timers into this mix might be a bit
>> much,
>> > but, then again, if we're graduating "Command" to core, then it seems
>> like
>> > it would be nice to make it the uniform callback interface.
>> >
>> > -- Bruce
>> >
>> >
>> >
>> > On Thu, Sep 3, 2009 at 9:28 PM, Bruce Johnson <br...@google.com> wrote:
>> > > I like it a lot Ray. (To be completely honest, I knew you were going
>> to say
>> > > all that, so I decided to sandbag and let you do the typing :-)
>> >
>> > > I question if it's really appropriate to explicitly say "PreEventLoop"
>> and
>> > > "PostEventLoop" considering that...sometimes...the event loop can
>> actually
>> > > run re-entrantly. Those names sound like a very strong guarantee that
>> I
>> > > don't think we can reliably guarantee. It's more like
>> > > "PreCurrentJavaScriptStackFullyUnwinding" and "PostEventLoop".
>> >
>> > > Actually, to take a step back (which is my very favorite thing to do),
>> > > there are several kinds of things that could be consolidated:
>> >
>> > > 1) Single-shot timers
>> > > 2) Recurring timers
>> > > 3) Incremental commands that run as soon as possible after the event
>> loop
>> > > (faster than setTimeout(0))
>> > > 4) Incremental commands that run after the event loop via
>> setTimeout(0)
>> > > 5) Deferred commands that run as soon as possible after the event loop
>> > > (faster than setTimeout(0))
>> > > 6) Deferred commands that run after the event loop via setTimeout(0)
>> > > 7)
>> Execute-this-before-you-unwind-the-JS-stack-in-which-it-was-enqueued
>> > > (aka BatchedCommand)
>> > > 8) Arguably, runAsync (although it's purpose is so functionally
>> different
>> > > it would probalby be a mistake to munge it in)
>> >
>> > > #3 and #5 might look funny, but it is generally possible to run code
>> > > *after* the event loop but *much* sooner than setTimeout(0), which is
>> > > usually clamped to some pretty long duration such as 10ms. The reason
>> you
>> > > wouldn't want to do #3 and #5 as the default for deferred commands is
>> that
>> > > it would keep the CPU overly busy if you did it a bunch in a row. It
>> would
>> > > very likely drain mobile batteries quickly, even.
>> >
>> > > @Ray (or anyone): Can you think of an awesome way to reconcile those
>> behind
>> > > a consistent API?
>> >
>> > > On Thu, Sep 3, 2009 at 4:52 PM, Joel Webber <j...@google.com> wrote:
>> >
>> > >> ++(++Ray)
>> > >> Anything we can do to sensibly get this crap out of .user and into
>> .core
>> > >> (or some other common location) would be very, very good.
>> > >> If, as a side-effect, we could get DeferredCommand to *not* use
>> > >> IncrementalCommand (the latter brings in fairly significant
>> dependencies
>> > >> that are enough to matter for small apps), that would be even better.
>> >
>> > >> On Thu, Sep 3, 2009 at 4:46 PM, Scott Blum <sco...@google.com>
>> wrote:
>> >
>> > >>> ++Ray.
>> >
>> > >>> On Thu, Sep 3, 2009 at 4:38 PM, Ray Ryan <rj...@google.com> wrote:
>> >
>> > >>>>   The mechanism is just brilliant. I have reservations about the
>> api.
>> >
>> > >>>> <bikeshed>
>> > >>>> "it seemed kinda nice to have one less type"
>> >
>> > >>>> Except that we have one more type, BatchedCommand, which looks
>> exactly
>> > >>>> like Command, except with a different name, and you have to
>> subclass it
>> > >>>> rather than implement it...
>> >
>> > >>>> A simple thing we could do is:
>> >
>> > >>>>    - create com.google.gwt.core.client,
>> > >>>>    - change com.google.gwt.user.client.Command to extend the new
>> one
>> > >>>>    - deprecate com.google.gwt.user.client.Command
>> > >>>>    - And have BatchedCommand accept com.google.gwt.core.client
>> >
>> > >>>> And the two names, "DeferredComand" and "BatchedCommand", don't
>> give
>> > >>>> much clue as to which does what. And of course BatchedCommand
>> doesn't
>> > >>>> actually provide any batching service.
>> >
>> > >>>> If we were doing all this from scratch, I suspect we would wind up
>> with
>> > >>>> something like this in core (presuming we're happy with
>> IncrementalCommand
>> > >>>> and addPause):
>> >
>> > >>>>     package com.google.gwt.core.dispatch
>> >
>> > >>>>     public interface Command {
>> > >>>>       void execute();
>> > >>>>     }
>> >
>> > >>>>     public interface IncrementalCommand {
>> > >>>>       boolean execute();
>> > >>>>     }
>> >
>> > >>>>     public class PreEventLoopDispatcher {
>> > >>>>       public static PreEventLoopDispatcher get(); { ... }
>> >
>> > >>>>       public void addCommand(Command c);
>> > >>>>     }
>> >
>> > >>>>     public class PostEventLoopDispatcher {
>> > >>>>       public static PostEventLoopDispatcher get(); { ... }
>> >
>> > >>>>       public void addCommand(Command c);
>> > >>>>       public void addCommand(IncrementalCommand c);
>> > >>>>       public void addPause();
>> > >>>>     }
>> >
>> > >>>> Note the avoidance of statics to make commands more testable, a
>> > >>>> recurring subject.
>> >
>> > >>>> Seems like we could do this, deprecate the existing classes, and
>> make
>> > >>>> them wrappers around the new.
>> >
>> > >>>> </bikeshed>
>> >
>> > >>>> On Wed, Sep 2, 2009 at 11:36 PM, Ray Cromwell <
>> cromwell...@gmail.com>wrote:
>> >
>> > >>>>> Could this also be used as a general pattern to batch DOM updates
>> from
>> > >>>>> multiple Widgets performing updates? e.g. a current approach to
>> avoid the
>> > >>>>> overhead, of say, installing a dozen widgets, is to concatenate
>> all the HTML
>> > >>>>> together, slam it into innerHTML, and then wrap the widgets around
>> the HTML.
>> > >>>>> But this rather breaks the nice OO design people are used to with
>> widgets.
>> > >>>>> Templating is an alternative, but I'm wondering, why can't we make
>> all of
>> > >>>>> the attachment stuff happen via a batch queue. A special optimizer
>> on the
>> > >>>>> queue could even recognize instances of when DOM updates can be
>> coalesced
>> > >>>>> and leverage documentFragment or innerHTML.
>> > >>>>> e.g.
>> >
>> > >>>>> VerticalPanel vp = ...
>> > >>>>> vp.add(new Label())
>> > >>>>> vp.add(new Label())
>> >
>> > >>>>> The objects are constructed, but the HTML mutations are
>> > >>>>> deferred/queued. When adding a DOM mutation to the queue, you
>> could check if
>> > >>>>> existing queue data isOrHasChild the new DOM mutation element, and
>> if so,
>> > >>>>> just modify the queue element (coalesce) rather than appending
>> another queue
>> > >>>>> item. Then, when processing the queue, you only need to add the
>> roots to the
>> > >>>>> DOM, attaching/modifying enmasse.
>> >
>> > >>>>> This would preserve the OO-ness of constructing widget hierarchies
>> > >>>>> without requiring 'foreign' string-based templating.
>> >
>> > >>>>> -Ray
>> >
>> > >>>>>  On Wed, Sep 2, 2009 at 5:13 PM, Bruce Johnson <br...@google.com
>> >wrote:
>> >
>> > >>>>>>  On Wed, Sep 2, 2009 at 6:07 PM, Scott Blum <sco...@google.com
>> >wrote:
>> >
>> > >>>>>>> I do agree with John that we should really discuss how this can
>> be
>> > >>>>>>> implemented.
>> >
>> > >>>>>> It's already implemented!
>> >
>> > >>>>>>>  Is there some magic trick to make the browser execute a piece
>> of
>> > >>>>>>> code at the time you want, or do we need to go and modify all
>> our event code
>> > >>>>>>> (like with the global uncaught exception handler)?
>> >
>> > >>>>>> No trick, it's as bad as you'd hope it wasn't. On the positive
>> side,
>> > >>>>>> it's already been done -- I'm just augmenting the tests for the
>> various
>> > >>>>>> subsystems such as RequestBuilder and event dispatching to make
>> sure we
>> > >>>>>> tighten the correctness noose as much as possible.
>> >
>> > >>>>>> Longer term, Bob and I both would really like to find a general
>> > >>>>>> mechanism for making this pattern easy to do from any path into a
>> GWT module
>> > >>>>>> from "the outside", exactly along the lines of what Matt was
>> talking about.
>> > >>>>>> I think rolling this functionality into gwt-exporter (and then
>> rolling that
>> > >>>>>> sort of functionality directly into GWT proper) will get us
>> pretty far down
>> > >>>>>> the road.
>> >
>> > >>>>>> Code review request forthcoming, possibly tomorrow.
>> >
>> > >>>>>> -- Bruce
>>
>> >>
>>
>

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to