As is our team custom, I'd like unanimous signoff (or FHMP) from the GWT
team on this new API, as well as any and all feedback from the larger
community.
=== Proposal ===
Add a new API called BatchedCommand that enqueues callbacks that should be
called "later", much like a DeferredCommand, but definitely prior to the
next dispatch of the native event loop.
=== Key use case: Style injection ===
Injecting CSS styles programmatically (e.g. StyleInjector) causes the
browser to do a lot of work (e.g. selector matching, assimilating the new
CSS with the existing CSS) and this work happens synchronously during the
"inject" call. Therefore, a sequence of such injections across independent
calls in GWT code tends to induce a huge amount of overhead in the browser
(that is, because the browser does so much recalculation for each call). It
is much faster to concatenate each small chunk of CSS across calls to
"inject" into one large CSS chunk so that the overhead need only occur once
when all the CSS text has been concatenated (vs. doing the work once per
"inject" call site). Measurements have show 8-10x difference in speed
between the two approaches on Chrome and other browsers. (I'm not being
especially precise in the timings because the savings is large enough that
there's simply no question that it's beneficial).
The above describes exactly the sort of thing that DeferredCommands are
perfect for, except for one thing: DeferredCommands are guaranteed to run
*after* the JavaScript causing them to be enqueued has finished and the
native event loop has executed at least once. In the case of
programmatically injected styles, we definitely do not want this behavior,
because it would mean that DOM structures created could be rendered (because
the event loop might dispatch paint handlers) before their associated CSS
has been injected. The net results would be the user seeing ugly unstyled
DOM structures for at least a short amount of time, which is unacceptable.
Thus, we need a BatchedCommand that is guaranteed to execute callbacks
*before* the JavaScript call stack in which they are enqueued has fully
unwound. A prototype version of StyleInjector has been created that uses the
new technique, and it works nicely.
=== The Actual API ===
It is small enough that I can include the code directly:
package com.google.gwt.core.client;
/**
* A <code>BatchedCommand</code> is a command that will be executed by the
GWT
* libraries before the JavaScript stack unwinds (before the browser's
* native event queue is pumped whenever feasible).
*/
public abstract class BatchedCommand {
/**
* Enqueues a BatchedCommand to be executed before the JavaScript stack
* unwinds (typically before the browser's native event queue is pumped).
*
* @param cmd the command to be fired. Must not be <code>null</code>.
*/
public static void add(BatchedCommand cmd);
/**
* Causes the <code>BatchedCommand</code> to execute its encapsulated
* behavior.
*/
public abstract void execute();
}
As you can see above, it is in the "core.client" package. It is necessary to
put it here (vs. alongside or integrated with DeferredCommand) because we
have to guard every known entrypoint into Java code, which includes things
like runAsync callbacks, which are themselves in core. In other words,
putting BatchedCommand in gwt-user would have meant that core code would
have to depend on gwt-user, which would be a bad idea.
The last note is that the API pattern differs from DeferredCommand/Command
and IncrementalCommand in that it doesn't have a separate callback
interface. BatchedComand#add() doesn't take a "Command" callback; it takes a
BatchCommand which has overridden the execute() method. This isn't a vitally
important design divergence from DeferredCommand/Command, but it seemed
kinda nice to have one less type.
Okay, that's my story. Thoughts?
-- Bruce
P.S. Bob V has already signed off on this; we pair-programmed the prototype
code for it.
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---