Complex client-side javascript problem, need help

D

Derek

Hi,

I've built a rather large CGI that dumps a lot of data and a fairly
complex javascript app out to the client's browser. Granted this may
be poor style according to someone web design philosophy but that is
the way things need to work for now here. The problem I'm having is
that it appears that the browsers (IE, mozilla and netscape) are
sometimes getting confused about wether the javascript code is
running. By this I mean when I click a button that runs some filtering
functions on the data that was sent over by the CGI there are times
when it can take 10-15 seconds before the function finises executing.
But the filtering function always exits properly and when it does so
CPU utilization goes from 100% back to a normal 2-4% on my machine. At
this point I can continue to interact with the javascript app in my
browser, but sometimes the progress indicator in the status bar will
still keep moving as if processing is continuing in the background.
Also on occasion I've seen an error message pop-up (sometime during
but mostly after the filtering function) which says that the
javascript is making the browser run slowly do you want to continue.
After seeing this message the function has usually finished its work
so you click on yes to continue and everything is fine.

So, is there some way I can help the browser not loose track of the
fact that the javascript function has already returned?

And also is there a way to disable/prevent the browsers from
displaying the message about the javascript code causing the browser
to run slowly?

Finally what is the best way to give the users visible feedback that
the filtering function is working and that things aren't locked up?
The cursor doesn't change to the hourglass when the function is
running. And when I tried to output to another frame some progress
messages none of that data appeared in the frame until after the
filtering function had returned.

Thanks in advance for any help.

Derek
 
Y

Yep

Finally what is the best way to give the users visible feedback that
the filtering function is working and that things aren't locked up?
The cursor doesn't change to the hourglass when the function is
running. And when I tried to output to another frame some progress
messages none of that data appeared in the frame until after the
filtering function had returned.

You could (1) split the main job in little jobs (easy if you have some
kind of a loop in your main job) and (2) chain the job parts calls
with timeouts, in order for the UI to get updated and the browser not
to get terrified at how fast your script is eating memory :) To put
it another way, you'd give the browser some time to breathe while
working. The following quick demo should demonstrate the technique,
you'll have to make the split and find a more elegant way to inform
the user, though.


<script type="text/javascript">
//emulate the job part
function JobPart(){ /1(.+)+1/.test("011000"); }

// emulate the job chain
var A_Jobs=[]
for(var ii=0; ii<100; ii++)
A_Jobs[ii]=JobPart;

// job controller
var Job = function() {
var ii=0, d=document;
var UserInform = {
start : function() { c("wait"); window.status="0%"; },
update : function(state) { window.status=state+"%"; },
stop : function() { c("default"); window.status=""; }
};
var c=function(a){document.body.style.cursor=a;};
return function (){
UserInform.start();
A_Jobs[ii++]();
UserInform.update(Math.floor(ii*100/A_Jobs.length));
if (ii<A_Jobs.length) setTimeout(arguments.callee, 1);
else {
ii=0;
UserInform.stop();
}
}
}();
</script>

<input type="button" value="Lauch job" onclick="Job()">


HTH
Yep.
 
Y

Yep

mehdi amini said:
It looks great but would you (or others) mind to explain this peace of code
a bit more?

Sure; the goal was to expose the setTimeout method to the OP, and the
interest of using it recursively to form a chain of timed calls; if
you want to know more about this, check the archives at clj for
"setTimeout" and "setInterval", especially Richard Cornford
'TimedQue', for instance at

<URL: http://groups.google.com/[email protected]&rnum=3>

As for the code, explanations follow.

This part is used to emulate a job part; in reality you'd have a big
loop, such as
for(var ii=0; ii<bigNumber; ii++) {
doStuff(ii);
}
The idea is to execute 10 or more little loops instead of the big one,
so something like
function doStuffPart(start, end){
for(var ii=start; ii<end; ii++){
doStuff(ii);
}
}
and call doStuffPart many times, with a timeout between each one in
order to give the browser the time to update the UI. The feasibility
depends on the stuff to be done :)

The regexp used in the JobPart function is a killer for a regex
NFA-based engine, as javascript's. Adding more trailing zeros to the
string being tested can slow down the script, and I've found it a
quick way to emulate a consequent jobPart in a short line.
// emulate the job chain
var A_Jobs=[]
for(var ii=0; ii<100; ii++)
A_Jobs[ii]=JobPart;

An array of jobs; since there is only one function, this would be
neater to just call the function and throw the array away, but I had
before experimented with different jobPart funcs, and decided to leave
the array construction so that different JobParts could be called
without problem if needed.

The following part is more complicated, I've adopted a closure
approach, since I considered the two previous parts to be "only"
simple emulation and the following one to be the real issue (so
conception mattered) - moreover closures have been discussed a lot
recently and as a result I've come to use them quite often (thanks
Richard) :)

To know more about closures, check the archives, especially the
following thread "Closures, what are they good for":

// job controller
var Job = function() {
var ii=0, d=document;
var UserInform = {
start : function() { c("wait"); window.status="0%"; },
update : function(state) { window.status=state+"%"; },
stop : function() { c("default"); window.status=""; }
};
var c=function(a){document.body.style.cursor=a;};
return function (){
UserInform.start();
A_Jobs[ii++]();
UserInform.update(Math.floor(ii*100/A_Jobs.length));
if (ii<A_Jobs.length) setTimeout(arguments.callee, 1);
else {
ii=0;
UserInform.stop();
}
}
}();
</script>

The Job variable is assigned the _result_ of an anonymous function,
executed at the same time it is declared. This result appears to be a
function, but since this function is nested inside the executed one,
the local variables of the outer function are still available to the
inner function (the one that does the work), but completely hidden to
anything else - they are perfect private static members.

These members include "ii", used as an index for the JobParts array,
"d" as a pointer to document (keeping reference not only makes the
code faster and neater, but also easier to post in a NG, where you
have to limit code to 72 chars to avoid line breaks!).

UserInform is a simple object used as an interface to the UI, with
three methods (start, update and stop). The controller just calls the
appropriate methods to update the UI, if you want to remove the
terrible window.status with DHTML you just have to replace the content
of the methods.

"c" is a simple helper to update the cursor, as wanted by the OP.

The inner anonymous function (the one returned and being assigned the
the "Job" variable) does the whole job: lauching the job parts one
after the other, using private static vars to keep track of where it
is (it needs to keep the information outside of its own scope,
otherwise the info will be cleared as soon as the function has
executed). The setTimeout part is the real thing to understand, with
the function calling itself recursively (note how the scope chain
remains unaltered). The "1" timeout value will result in the function
being called as soon as possible (around 10msec in Win2k and 50ms on
Win98) - and this is enough to give the browser the opportunity make
the relevant UI updates and keep the script as fast as possible.

Of course adding such a "monitoring" feature will slow down the whole
script (be it table sorting, high calculations etc), but the benefit
for the user is invaluable in the end.


HTH
Yep.
 
R

Richard Cornford

... - moreover closures have been discussed a lot
recently and as a result I've come to use them quite
often (thanks Richard) :)
<snip>

You are welcome! But, do you remember a thread where we were discussing
my use of an expando to mask a global public static method so that
subsequent event handling calls would scope resolve to the expando. In
relation to some aspect of that script you asked me if I thought using
closures instead had any problems. At the time I didn't know, I had not
used, and did not sufficiently understand closures, but I thought I had
better find out. (Actually I though; If Yep is interested in closures
they must be interesting so I will find out how and why. :)

So, thank you, for setting me of in pursuit of closures and into one of
the most interesting areas of JavaScript (and easily my favourite now).

Incidentally, I recently re-wrote a closure-based nested object DHTML
script into a standard public member/prototype script that did the same
processor intensive animation operations so that I could directly
compare accessing private instance members (as outer function local
variables) with accessing the same values as public members via the -
this - keyword. It seems (on all the browsers that I tested) that the
interpreter resolves outer function local variables faster than it can
access public members using the - this - keyword. So there is a manifest
advantage in using closures in performance critical scripts (Traded off
against increased memory use and slightly greater time to initialise the
objects due to the need to create more function objects).

Next is regular expressions (I wonder why ;-). I don't use them much, I
don't understand them sufficiently, but give me 6 months ...

Richard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,905
Latest member
Kristy_Poole

Latest Threads

Top