views:

85

answers:

5

I am getting a "too much recursion" error. I will try to give a clear analog to my actual application where this problem is occurring.

Imagine that the app is trying call a webservice to get information about the contents of a very long freight train. There is so much information that we cannot simply invoke the webservice and tell it to send down everything all at once about every car in tthe entire train. Rather, we ask the webservice about each freight-car in turn, and when the info about a single boxcar is received, we update the page and then ask for the next car.

To keep the client responsive, we avoid a loop and use events with array.shift() instead:

We have an event:

$('body').bind('NextBoxCar', function( e,  p) { getNexBoxCar(e,p);  });

We have a function that removes the first boxcarid from the array and sends it to the webservice:

 EDIT: BoxCarIds is not a global but inside a function
 function() foo {
     var BoxCarIds = [123, 222, 767, 1234, 9298, ...  999];
     $('body').triggerHandler('NextBoxCar', BoxCarIds);
 }

 function getNextBoxCar(e, BoxCarIds) {      

   var id =  BoxCarIds.shift();

   // build an ajax request; omitted for brevity
     $.ajax( {
    .
    . <snip>
    .
     success:  function(data, textStatus, oHTTP ) {
        UpdatePage(data, BoxCarIds);
     }
   });

 }

When the data for the boxcar comes back from the webservice, the ajax success-handler invokes the following function, which updates the page and then fires the NextBoxCar event. I believe this is where the recursion problem is happening.

 function UpdatePage(data, BoxCarIds) {

     // update the page with data
      .
      . <snip>
      .

     // if there are still ids remaining in the array, we need to call the webservice again by firing the event

     if (BoxCarIds.length > 0 ) {
       $('body').triggerHandler('NextBoxCar', BoxCarIds);

     }


 }

How to avoid the recursion problem?

Thanks

+2  A: 

You have BoxCarIds as a global variable, but you are also passing it in as a parameter to UpdatePage and getNextBoxCar. So, when you are inside UpdatePage or getNextBoxCar, you are manipulating the local version of BoxCarIds, not the global one!

Remove the parameter BoxCarIds from UpdatePage and getNextBoxCar. Then your code should work fine.

Below is an example:

var x = 3;

function Eat(x)
    {
        x = 5;
    }

alert(x); //alerts 3
Eat(x);
alert(x); //alerts 3

But if you remove the parameter from Eat, you get:

var x = 3;

function Eat()
    {
        x = 5;
    }

alert(x); //alerts 3
Eat(x);
alert(x); //alerts 5
EndangeredMassa
@EndanderedMassa: Thanks. I've corrected the question to show that BoxCarIds was actually not a global. But let's say I remove the second parameter from the event args and track that array a global instead. We still get the too much recursion error in FF; it's the event recursion that appears to be causing the problem in FF.
Tim
Ah, sure. You can still recur too many times depending on the size of the array. I'll post a different answer.
EndangeredMassa
A: 

I don't see an Edit button, so I will have to respond to the suggestions with an Answer. No way to format code to make it readable in the Comment.

I goofed when asking the question above. Apologies. BoxCarIds in the actual program isn't global. The program is like this:

 function getBoxCarIds(){
     .  
     .
     .


     var BoxCarIds = data;   // data is returned by webservice
     $('body').triggerHandler('NextBoxCar', BoxCarIds);   // kick things off


 }

The length of the BoxCarIds array is actually diminishing. When the "freight train" is fairly short, the program runs fine. It's only encountering the error when the train is a long one (e.g. 140 cars).

Tim
And the error is here in jquery (@ line 55)if(!b){d=a.type.split(".");a.type=d.sh...nt view wheelDelta which".split(" "), http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.jsI will try to download the un-min version for a clearer idea of what's going on.
Tim
And it seems to be happening only with FF 3.6.6 (Chrome + Opera are fine).
Tim
A: 

The 'too much recursion' error is happening in FF 3.6.6 at line 1936 in jquery 1.4.2:

  // Filter the functions by class
 1929 if ( all || namespace.test( handleObj.namespace ) ) {
 1930 // Pass in a reference to the handler function itself
 1931 // So that we can later remove it
 1932 event.handler = handleObj.handler;
 1933 event.data = handleObj.data;
 1934 event.handleObj = handleObj;
 1935
 1936 var ret = handleObj.handler.apply( this, arguments );   // ERROR HERE
 1937
 1938 if ( ret !== undefined ) {
 1939 event.result = ret;
 1940 if ( ret === false ) {
 1941 event.preventDefault();
 1942 event.stopPropagation();
 1943 }
 1944 }
 1945
 1946 if ( event.isImmediatePropagationStopped() ) {
 1947 break;
 1948 }
 1949 }
 1950 } 
Tim
A: 

And in Firefox 4.0 beta 1 the error is happening at line 1595:

/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code originated from
 * Dean Edwards' addEvent library.
 */
jQuery.event = {

// Bind an event to an element
// Original by Dean Edwards
add: function( elem, types, handler, data ) {
    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
        return;
    }

    // For whatever reason, IE has trouble passing the window object
    // around, causing it to be cloned in the process
    if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
        elem = window;
    }

    var handleObjIn, handleObj;

    if ( handler.handler ) {
        handleObjIn = handler;
        handler = handleObjIn.handler;
    }

    // Make sure that the function being executed has a unique ID
    if ( !handler.guid ) {
        handler.guid = jQuery.guid++;
    }

    // Init the element's event structure
    var elemData = jQuery.data( elem );

    // If no elemData is found then we must be trying to bind to one of the
    // banned noData elements
    if ( !elemData ) {
        return;
    }

    var events = elemData.events = elemData.events || {},
        eventHandle = elemData.handle, eventHandle;

    if ( !eventHandle ) {
        elemData.handle = eventHandle = function() {       // ERROR HERE
            // Handle the second event of a trigger and when
            // an event is called after a page has unloaded
            return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                undefined;
        };
    }
Tim
+2  A: 

The recursive part here isn't important to the functionality. It's just a consequence of the ajax calls. You could make the ajax calls synchronous and change getNextBoxCar to a loop. However, that will still leave the page unresponsive!

So, I think that you can fix the issue by using a setTimeout to call the UpdatePage method. This should break the recursion and leave the page responsive. See this jsFiddle example.

Try changing to this:

// build an ajax request; omitted for brevity
$.ajax( {
    .
    . <snip>
    .
    success:  function(data, textStatus, oHTTP ) {
        setTimeout( function() {
            UpdatePage(data, BoxCarIds);
        }, 0);
    }
});
EndangeredMassa
Thank you for the setTimeout tip. It solves the problem.
Tim