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 -~----------~----~----~----~------~----~------~--~---