12

I have an event, which can fire itself. I try to make the code as efficient as possible, but it can hit maximum call stack in some circumstances, which are out of my control. It's not an infinite stack and it will end at some point, but sometimes it can potentially crash before it finishes because of the limit.

Will I increase the number of call stack if I set up 2 similar event listeners and split the code? Or what can I do?

UPDATE: It's on DOM change event (working with Webkit only, so don't care about other browsers), which can also modify the DOM based on some conditions. I haven't really hit that limit yet, but theoritically, it potentially can. I'm still optimizing the code to make as less DOM manipulations as possible.

UPDATE 2: I'm including sample (not real) example:

document.addEventListener('DOMSubtreeModified', function(event){
            
    this.applyPolicy(event);
            
}, true);

function applyPolicy(event){
    if( typeof event != "undefined" ){
        event.stopPropagation();
        event.stopImmediatePropagation();
    }
    
    if( !isButtonAllowed ){
        $('button:not(:disabled)').each(function(){
            
           $(this).attr('disabled', true);

        });
    }
}

This is just a sample code, but even in this case, if you have say 100s of buttons, the call stack will be in 100s too. Note that if you use $('button').attr('disabled', true);, this will cause call stack problem, because jQuery will be trying to modify the DOM infinitely.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Sherzod
  • 5,041
  • 10
  • 47
  • 66
  • 1
    Perhaps you should convert your recursive function into a ``while`` loop? I can't imagine that you need the side-effect of actual hundreds-of-thousands of event triggers to fire some other function as well... –  Mar 22 '12 at 18:29
  • 1
    Never reached that limit, if not when erroneously coding a infinite-loop. Show the code.. – gpasci Mar 22 '12 at 18:30
  • 2
    Short answer: there is no (standard) mechanism to do this. The only option then, is to alter the code. –  Mar 22 '12 at 18:30
  • Have you considered that maybe you're trying to do things the wrong way? – Blazemonger Mar 22 '12 at 18:33
  • It's a on DOM change event (working with Webkit only, so don't care about other browsers), which can also modify the DOM based on some conditions. I haven't really hit that limit yet, but theoritically, it potentially can. – Sherzod Mar 22 '12 at 18:34
  • Okay, so *why* are you changing the DOM when the DOM is changed? I can't think of any good reason for that -- whenever you get some sort of input from the user or your remote server, you should make your DOM changes. I see that event as primarily helpful for debugging purposes, and I'd never use it the way you're describing. –  Mar 22 '12 at 18:44
  • That example doesn't tell me *why* you're doing that, just that it causes the problems that you've stated it causes. Explain what you're trying to accomplish and I'll answer with an alternative method to achieve the same results without the infinite recursion headache you're trying to mitigate. –  Mar 22 '12 at 19:03
  • @DavidEllis: The code will run together with the third party code, and we just need to make sure that they show what's "allowed" on the screen (eg. in some cases, buttons should be disabled, etc.). So I thought the only way of enforcing that policy is by checking whenever DOM changes and enforcing it if the 3rd party developer didn't follow it already. It's not really a website, but an app that runs on a closed environment with Webkit browser installed. – Sherzod Mar 22 '12 at 19:08
  • There's no _standard_ way of doing this, but if you're using it just for debugging, is it possible to do at all? – agentofuser Nov 25 '12 at 12:57
  • I'm trying to find the object path of a property that has a certain value in the DOM, and even though I've treated for circularity, I still hit the call stack limit. If would be really useful to just be able to increase it. More here http://stackoverflow.com/questions/13551321/how-do-i-find-the-full-path-to-an-object-that-has-a-property-with-a-given-value – agentofuser Nov 25 '12 at 13:08

6 Answers6

7

While it sounds like you may need to rethink some code, one possibility would be to put a recursive call in a setTimeout at some given interval. This allows you to begin a new call stack.

Take this example...

var i = 0;

function start() {
    ++i;
    var is_thousand = !(i % 1000);

    if (is_thousand)
        console.log(i);

    if (i >= 100000)
        return; // safety halt at 100,000
    else
        start()
}

It just logs to the console at every interval of 1,000. In Chrome it exceeds the stack somewhere in the 30,000 range.

DEMO: http://jsfiddle.net/X44rk/


But if you rework it like this...

var i = 0;

function start() {
    ++i;
    var is_thousand = !(i % 1000);

    if (is_thousand)
        console.log(i);

    if (i >= 100000) // safety halt at 100,000
        return;
    else if (is_thousand)
        setTimeout(start, 0);
    else
        start();
}

Now at every 1,000, the function will be allowed to return and the next call will be made asynchronously, starting a new call stack.

Note that this assumes that function is effectively ended when the recursive call is made.

Also note that I have a condition to stop at 100,000 so we're not infinite.

DEMO: http://jsfiddle.net/X44rk/1/

5

For any browser the maximum call stack runs well in the thousands. You should try to optimize the code, having a huge call stack is not good for speed and memory.

If you're running into this, it's an indicator your code is in dire need of reorganization

Evert
  • 93,428
  • 18
  • 118
  • 189
  • the thing is, the event is listening for DOM change, and after checking some conditions, it can actually modify the DOM, which will fire that event again. Because of that, call stack may increase pretty fast. I'm still working on optimizing the code to make as less DOM manipulations as possible. – Sherzod Mar 22 '12 at 18:31
  • Well, this is a very general comment I know.. but you need a clearer separation of concerns. If you listen to domchange events, it means you kind of rely on your DOM as a 'model' or 'view model'. There's a ton of things wrong with that. Try implementing backbone.js, even if you don't like that specifically.. you can learn a great deal from good UI design patterns. – Evert Mar 22 '12 at 18:37
  • I'm using JavascriptMVC and jQuery. The only way of reducing the recursion in this case is to reduce the number of DOM manipulations, so that it doesn't fire itself so often.. is there a better way? – Sherzod Mar 22 '12 at 18:41
  • Adding an event to dom manipulation is a bad thing by itself. In an ideal case the code that updates the DOM should trigger *also* trigger an internal event. Don't let your events flow through your DOM, it should be a bit of a one-way street. – Evert Mar 22 '12 at 18:45
  • 1
    If JavascriptMVC encourages this pattern, I'd have serious doubts about this particular framework. (speaking as someone who's done public speaking about this very subject) – Evert Mar 22 '12 at 18:46
  • JavascriptMVC doesn't encourages this at all. This is just something I came up with. The code will run together with the third party code, and we just need to make sure that they show what's "allowed" on the screen (eg. in some cases, buttons should be disabled, etc.). So I thought the only way of enforcing that policy is by checking whenever DOM changes and enforcing it if the 3rd party developer didn't follow it already. – Sherzod Mar 22 '12 at 18:56
  • 2
    If it's really a 3rd party you're dealing with, then I agree with you.. it may be the only way to deal with such things. In that case I would try to ensure that your events cannot be triggered recursively. When your first DOM change event triggers, you could temporarily disable this event, and only turn it on again *after* you completed this event. This will make it impossible for the stack to grow out of control. P.S.: I enjoy talking about stack overflow problems on stackoverflow ;) – Evert Mar 22 '12 at 19:09
  • "dire need of reorganization" or "optimization" is jumping to conclusions about something, you can have no idea about. Recursive designs are becoming increasingly more common in asynchronous patterns and simple programming errors, such as a faulty termination, does not qualify as in "need of reorganization", restructuring or refactoring. – Thomas Williams May 29 '23 at 09:30
  • @ThomasWilliams not sure if my last comment got deleted, but I got another notification, so I'll share again: you're arguing with a 11-year old opinion. Re-reading my opinion, I think it still stands, but I have some nuance. Don't write recursive code that recurses this deep in a language that doesn't have tail call optimization. this *should* be rewritten using a stack-based algorithm in languages like Javascript. Surprised that someone with your experience would argue this. – Evert May 31 '23 at 06:22
  • @Evert I think we are discussing an edge case here, I agree that the specific case calls for a different design, no doubt and I agree with your first paragraph. But imho your conclusion is off. "Running into this" is a wide assumption, unlimited by time, and could simply indicate a recursive algorithm is not terminating due to poorly defined conditions - in a scenario _calling_ for a recursive algorithm (f.ex. traversing a node-structure). Context: A headline saying "How to increase the maximum call stack in Javascript?" _likely bc a request in the developer console to increase the call-stack_ – Thomas Williams Jun 01 '23 at 08:54
0

The size of the callstack and implementation details will depend on the JavaScript environment your code is running in. Even if you can manipulate stack usage to run in environments you care about now, there's a great chance that stack-hungry code will crash when run in an environment that provides a different stack size.

Any recursive program can be rewritten as an iterative one. Consider if you can do that in your case. See:

Way to go from recursion to iteration

Community
  • 1
  • 1
Eric J.
  • 147,927
  • 63
  • 340
  • 553
0

You can't, they are browser dependant and quite frankly they have quite a wide range, so no need to worry about that IMO.

Starx
  • 77,474
  • 47
  • 185
  • 261
0

If you're hitting the call stack limit, you almost certainly have a recursive series of events. Events rely on a stack, so they're really not the best way of implementing a loop. In a language without tail call elimination, your only real option is to use the standard loop constructs like for/in.

Zack Bloom
  • 8,309
  • 2
  • 20
  • 27
0

After finding out that you're censoring third-party code that may be rendering elements you don't want rendered, I understand the real root problem: Blacklists never work on untrusted code, eventually some code will get past your blacklist and you're done for.

Outside of rearchitecting your code such that no third-party code expects (or is given) access to the DOM, my solution would be as follows:

  1. Duplicate your DOM into a hidden iframe.
  2. Sandbox the third party code in said iframe.
  3. On any DOM change in the iframe, inspect the iframe for differences between the two DOM trees. If the change passes a whitelist, pull it up into your real DOM (re-attaching event callbacks and so on).
  4. Copy the structure of the "real" DOM after the updates into the iframe DOM, scrubbing any sensitive data from it in the process.

You'd still check on a DOM event in the iframe, but you wouldn't in your "actual" page, so you can't enter an infinite loop.

This is assuming that you really can't trust your third party code to do what it should do. If it's just vendor incompetence, forget the scrubbing portion, but stick with the whitelist instead of the blacklist, anyways.

  • The code itself is analyzed by a different script before publishing, but 3rd party developer might have forgotten to disable certain things on some event, hence, I need to check it and enforce that policy. I will definitely try to create something really simple using the method you mentioned. Idea of iframes sounds interesting, although I hate them. Thanks! – Sherzod Mar 22 '12 at 20:55