Complex client-side javascript problem, need help

Discussion in 'Javascript' started by Derek, Aug 19, 2003.

  1. Derek

    Derek Guest

    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
     
    Derek, Aug 19, 2003
    #1
    1. Advertisements

  2. Derek

    Yep Guest

    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.
     
    Yep, Aug 20, 2003
    #2
    1. Advertisements

  3. Derek

    Yep Guest

    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/groups?hl=...bdqk4h$d3v$1$&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.
    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":

    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.
     
    Yep, Aug 22, 2003
    #3
  4. <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.
     
    Richard Cornford, Aug 23, 2003
    #4
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.