2

I am attempting to run the following while loop, but for some reason am getting a JS heap out of memory error:

 while( typeof(apiResults[0].league.season.eventType[0].events[0].pbp[i].pbpDetails[p]) == 'undefined') {
    //create time delay of one second
    setTimeout(function(){
        //put code to run after delay here
        if( typeof(apiResults[0].league.season.eventType[0].events[0].pbp[i+1].pbpDetails[0]) != 'undefined') {
            //this will run if the next inning has come through
            i = i+1;
            p = 0;
        }
            //this will also pass the while loop and go down to the actual code
    }, 3000);
}

I know the logic is flawed, but right now I am only running it in instances where the if statement will be true.

I receive the following error:

<--- Last few GCs --->

11450 ms: Mark-sweep 1388.5 (1433.0) -> 1388.5 (1445.0) MB, 834.3 / 0.0 ms (+ 0.0 ms in 63 steps since start of marking, biggest step 0.0 ms) [allocation failure] [scavenge might not succeed]. 12374 ms: Mark-sweep 1400.2 (1445.0) -> 1400.3 (1445.0) MB, 910.6 / 0.0 ms (+ 0.1 ms in 188 steps since start of marking, biggest step 0.0 ms) [allocation failure] [scavenge might not succeed].

<--- JS stacktrace --->
Cannot get stack trace in GC.
FATAL ERROR: MarkCompactCollector: semi-space copy, fallback in old gen Allocation failed - JavaScript heap out of memory
 1: node::Abort() [/usr/local/bin/node]
 2: node::FatalException(v8::Isolate*, v8::Local<v8::Value>, v8::Local<v8::Message>) [/usr/local/bin/node]
dgelinas21
  • 641
  • 3
  • 9
  • 22

3 Answers3

7

I don't think this does what you would expect. setTimeout takes the function that you pass it and after the delay, adds the function to the stack after everything else thats there. Since the while loop blocks, you end up with a weird situation where the delayed function won't get called until the while loop finishes.

var x = 0;
while (x<10) {
  x++;
  console.log('omg');
  setTimeout(function(){console.log('from timeout');},0);
}

What you will see above is that the loop will continue until it's done before any of the setTimeouts execute. If your loop is infinite then it creates a huge stack of calls that need to happen in the future but can't because they are blocked by the loop.

If I had to guess, it seems like you are implementing some sort of long polling thing. if that thing is synchronous (seems like it isn't) it could be easier to poll it. If it's not, then you will have to think about handling this asynchronously. Either with callbacks, events or promises.

In a situation where you are waiting for something to happen events are often your friend. You can create custom events or use frameworks to help propagate for you.

ktilcu
  • 3,032
  • 1
  • 17
  • 15
  • Right, off the current callstack. it cant run until the callstack is empty. @Jonasw – Kevin B May 11 '17 at 18:16
  • @Jonasw I won't claim that I use all the right words. It's more of how I understand it. My main point is that the delayed functions will never be called if the loop continues. – ktilcu May 11 '17 at 18:20
  • @Jonasw thx for the review! I updated the answer to try to be more descriptive – ktilcu May 11 '17 at 18:24
3

If that i = i+1; in the delayed function is the only place in your code where i is changed, that would probably explain it. Your code will keep repeating and creating timers for setTimeout thousands of times since i can only change(which will advance the loop) 3 seconds later(assuming the timer isn't then delayed by the load from all the timers being created, which it probably will be).

You need to refactor this code to not use setTimeout in a loop.

Karl Reid
  • 2,147
  • 1
  • 10
  • 16
1

Based on KarlReids answer ( good explanation) you may want to try this:

var i=0;
while( typeof(apiResults[0].league.season.eventType[0].events[0].pbp[i].pbpDetails[p]) == 'undefined' && i<=apiResults[0].league.season.eventType[0].events[0].pbp.length) {
setTimeout(function(i){//notice the bound i...

    if( typeof(apiResults[0].league.season.eventType[0].events[0].pbp[i+1].pbpDetails[0]) != 'undefined') {
        //some code
    }

}, 3000,i);
i = i+1;//increase in the main loop
}

Or recursive ( one timeout after another):

(function loop(i){
    if( typeof(apiResults[0].league.season.eventType[0].events[0].pbp[i].pbpDetails[p]) == 'undefined' && i<=apiResults[0].league.season.eventType[0].events[0].pbp.length) {
        setTimeout(function(i){//notice the bound i...

             if( typeof(apiResults[0].league.season.eventType[0].events[0].pbp[i+1].pbpDetails[0]) != 'undefined') {
                 //some code
             }
             //next
            loop(i+1);
         }, 3000,i);
    }
})(0);//start with zero
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151