I know I've asked this before, but I am still clueless. Could anyone
explain just what these errors are and when they show up? What are
they trying to tell us? Where can we get more information about them
-- a stack trace, perhaps? What typically causes them? What is the
best way to debug them (firebug? alerts often don't help a lot as it
is often with asynch stuff)? Bottom line -- how do we get these to go
away? This might be inaccurate, but I think that one time a zen
assertion error went away when we fixed a javascript syntax error and
another went away when we switched to running something synchronously
IIRC.
Are we the only ones who get these regularly? I suspect we are
missing some key concept that causes us to hit these regularly,
typically causing hours of lost work and a general decline in our
spirits.
If no one has any answers on this, perhaps just send out a limerick or
something else to help me keep smiling. thanks. --dawn
--
Dawn M. Wolthuis
Take and give some delight today
The following should probably be an article -- lets discuss it and then
based on the discussion, I will post an edited version if that seems
useful. This document should incorporate all the information from
previous posts on this question. Your recollection is correct and the
two solutions that you have used before are explained more fully here.
The short answer is that it is not /always/ your fault when you receive
this message! The medium sized answer is that you should be getting this
message a lot more often using the current mechanism and the long
answer, well, the long answer is below.
================================================================
The 'refresh list should not be null' assertion occurs after
synchronizing the DOM on the client in
the call to zenEndDeferredRefresh. The 'refresh list' is a set of
changes to the DOM shipped to the
client. zenEndDeferredRefresh() signals the client-side objects to
redraw themselves based on the
changes from the client. This allows Zen to only redraw the page once
per 'refresh'.
The generated synchronization code looks something like this:
// %EndChangeTracking: sync client with server changes
try {
zenBeginDeferredRefresh('2','%ZEN.Component.tablePane.ReallyRefreshContents');
var o = zenPage.getComponent(27); //5@%ZEN.Component.tablePane
if (o) {
o.setProperty('lastUpdate','2008-11-26 19:15:36.357');
o.rowCount = '8';
o.snapshotId = 31600518;
o.fireOnUpdateEvent();
}
zenEndDeferredRefresh('2','%ZEN.Component.tablePane.ReallyRefreshContents');
}
catch(ex) {
zenExceptionHandler(ex,arguments,'A JavaScript error occurred in
%EndChangeTracking.');
zenEndDeferredRefresh('2','%ZEN.Component.tablePane.ReallyRefreshContents');
}
You will note that there are two ways for zenEndDeferredRefresh() to be
called, when the
synchronization completes successfully, and when an exception is thrown.
This leads to the first case where the assertion failure can happen:
If an exception is thrown in zenBeginDeferredRefresh() or
in the synchronization code itself.
To resolve this, it is necessary to use a tool such as FireBug for
FireFox to view the response
from the server. Alternatively, WireShark(wireshark.org) or tcptrace
(www.pocketsoap.com/tcptrace/) may
be used to capture the messages. It is generally obvious based on the
code sent to the browser
whether this is the problem.
The 'refresh list' is initialized in zenBeginDeferredRefresh() and is
cleared in
zenEndDeferredRefresh; so if zenEndDeferredRefresh() is called twice
during one refresh cycle (aka ZenMethod call),
then the second time through it will see the assertion failure. This
usually happens when. as a result
of one ZenMethod call, a user callback is invoked which makes a second
ZenMethod call; this causes
the deferred refreshes to be nested.
There is a mechanism which is supposed to stack these nested calls to
properly handle this case,
however we have recently discovered that this mechanism does not fully work.
[NOTE: In some cases this situation is due to
programmer error and the DOM synchronizations
could be incompatible. In most cases this is
fine, though not very efficient.]
To resolve this there are three basic strategies. The first, is to look
at the network or trace
messages and to eliminate any logic errors and try to remove multiple
trips to the server.
The second strategy, which can make the code more efficient as well, is
to detect callbacks which
are generated because of the refresh. For example, the tablePane
component has a rowChanged event
callback which is sent a reason code, programmatic row change events
(such as a refresh) have a reason
code of "" and therefore do not require the same actions to be run as
when the row change event was
triggered by the user.
The third stategy is to allow the second zenMethod call but to do one of
two options:
A) Save the internal flag used by the Deferred Refresh mechanism to
prevent recursive refreshes.
This can be done by saving the zenRefreshMode variable and setting it to
2 prior to making the
synchronous call. This will turn off refreshes so the original value of
this flag should be restored
immediately after the synchronous call.
B) Invoke the nested ZenMethod call "later". This is done by using the
zenSetDeferredAction(func,0)
technique to execute func as soon as possible (as soon as we are finished).
/// Set a deferred action.
/// A deferred action is fired after a specified delay. If another
/// deferred action is set in the meantime, the first is never fired.
function zenSetDeferredAction(func,delay)
This is an excellent technique to serialize the ZenMethod calls an
avoids any synchronization
issues that could occur. Depending on how the function passed in the
formal argument "func" is defined,
it may require an advanced JavaScript called a closure to keep track of
private variables.
I am not aware of any other causes of this error, so I hope that this
response will be helpful to
you. If you need assistance please do log a WRC ticket and support can
assist you
Best Regards,
Derek Day
Dawn Wolthuis wrote (12/5/08 3:25 PM):
Any chance you could give an example of some javascript code, perhaps
a method with a table refresh, that illustrates one or more of these
ways to get rid of these zen assertions?
These often occur when the user moves to a new tab. I have not studied
your response enough yet to fully understand or know if there is
something we could do when we move to a new tab (we have an onshowTab
method) to help avoid these. I will study this over the weekend, but
if you have a thought on that, let me know.
Thanks a bunch! --dawn
Derek does indicate that it isn't _always_ "our fault", but I am in
complete agreement that these crop up too often and are really
difficult to nail down and fix or work-around the problem. I thought
they were turning my hair gray, but upon return from the hair dresser
-- nope, not a gray hair in sight (perhaps a work-around there).
I'll confess that my application-developer's mind doesn't have enough
understanding of what Derek is saying to know what actions to take
just yet, but I am very encouraged that I got an answer, and I have
confidence that if the conversation continues (maybe drops down to my
level, or "up" depending on perspective), we will get somewhere on
this front. There was even a hint in Derek's response that there might
be bug fixes for this in future releases, I think, maybe, perhaps??
In any case, glad to hear your voice on this topic too, Peter. This is
one headache I would really like to get beyond. Cheers! --dawn
Thanks,
Derek
pfc wrote (12/5/08 3:57 PM):
Intrigued by your sentence
" I found that dealing with those assertion failures that were easy to
reproduce forced me to write more efficient code"
Could you discuss this a bit please
Peter
Our code is not in production, but I thought I would indicate what
happens here since it can give more hints regarding the
developer-experience with this issue. This is often a "Friday problem"
for me since on Fridays I often spin through smaller tickets and
"nits" doing more minor code changes, trying to get as many tickets
turned around as I can. Yesterday I had just gotten to the point of
starting this process, later than I had hoped, when I changed a label
in a page for which I was not the primary author. I made the
label="text" change to that text, something the 20-something me would
not have tested, then ran a unit test. It worked, so I checked it in
and tested it in a verification account (what could fail, right?) in
our process to getting it staged for the next build. My payback for
being meticulous in my testing of a teeny tiny change is that the code
actually did fail. In that verification account I got the Zen
assertions. Arghhh. I am sure you can see how that can put a dent in
a developer's day. Total tickets closed yesterday: 0 (not just for
this reason, of course). And, once again, don't forget that there is
no happy hour here among the Calvinists in NW Iowa. smiles --dawn
Just to close off the discussion...
Part of my thought process for doing what I do is probably dated - I
evaluated the different options around 2 years ago
a. Trust myself....
This is still true - I am developing multi tabs each with very complex
logic/code to mix these into a single page would make it difficult to
maintain. Also would be concerned about the size resulting source code size
causing compilation problems (have seen others referring to this)
b. memory leaks....
I think things have probably improved in this area with new patches etc.
c. ISC Issues
In the original ZEN I believe there were problems, I remember an article
from Jon Pain on this ages ago - again things have improved.
So it really is (a) that is the main reason for continuing to do what I do
Also maybe another reason....
If it's all in a single page everything gets dl'd every time even if the
tabs are not used. Though this is a swings and roundabouts. What I do is
that every tab is called on clicking it so the first tab is quicker to dl
but there is a hit for each tab.
Peter
peter