15

Can a stack overflow be avoided in javascript by using the setTimeout method to call a function instead of calling it directly? My understanding of setTimeout is that it should start a new callstack. When i look in the callstack of both chrome and IE it seems that the setTimeout calls are waiting for the function call to return.

Is this just a property of the debugger or is my understanding flawed?

EDIT

While the answers provided below are correct, the actual problem I was having was related to the fact that I was calling setTimeout(aFunction(), 10) which was evaluating aFunction immediately because of the brackets. This question sorted me out.

Community
  • 1
  • 1
Aran Mulholland
  • 23,555
  • 29
  • 141
  • 228
  • The function passed into `setTimeout` cannot be invoked before the function that invoked `setTimeout` returned. So yes, that function does start a new callstack. – Šime Vidas Nov 08 '11 at 23:55

3 Answers3

18

I can confirm that the stack is cleared.

Consider this scenario:

function a() {
     b();   
}

function b() {
     c();   
}

function c() {
    debugger;
    setTimeout( d, 1000 );
}

function d() {
    debugger;
}

a();

So there are two breakpoints - one at the beginning of function c, and one at the beginning of function d.

Stack at first breakpoint:

  • c()
  • b()
  • a()

Stack at second breakpoint:

  • d()

Live demo: http://jsfiddle.net/nbf4n/1/

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • thanks for pointing me in the right direction, I was able to solve based on your answer, however I have another question see http://stackoverflow.com/questions/8058996/why-does-calling-settimeout-with-parenthesis-not-start-a-new-callstack – Aran Mulholland Nov 09 '11 at 00:34
  • If this guy is correct, how can your answer be the right answer? Because this guy is saying that it always pushes your callback to the callback queue first, not the stack and so if there are synchronous command still running in the call stack, OR other things in the stack it has to wait even more than the specified milliseconds. So it's not always a 1:1. This could happen: hey I gave you 5 second delay why are you coming back in 7? because the callback had to wait a little more. https://youtu.be/8aGhZQkoFbQ?t=782 – PositiveGuy Apr 24 '16 at 03:32
  • The delay that you specify via the second argument of `setTimeout` does not guarantee that the callback will be called at that specific point in time, true; but my answer doesn’t say that it would. – Šime Vidas Apr 24 '16 at 16:14
  • I think it's important to note that `d()` will be called with the scope of `c()` in a separate call stack. So though `c()` has finished all the way to `a()` when `d()` executes, their local variables are still held in memory until `d()` has finished and the GC gets around to clearing space. – Evan Kennedy Feb 07 '17 at 22:04
5

Async invocations, such as those from setTimeout, do indeed generate a new callstack.

It's not entirely clear what you're describing when you say "When i look in the callstack of both chrome and IE it seems that the setTimeout calls are waiting for the function call to return." But, one thing you can do is put a breakpoint inside of a function called by setTimeout, and see that the callstack is empty.

Domenic
  • 110,262
  • 41
  • 219
  • 271
  • Do you know why I can still see the entire callstack in the debugger? Is it because my function calls from setTimeout use a closure to get at certain local variables? – Aran Mulholland Nov 08 '11 at 23:55
  • 1
    @AranMulholland Where do you invoke the debugger? Inside the function that is passed into `setTimeout`? – Šime Vidas Nov 09 '11 at 00:01
  • @ŠimeVidas as per the question, I am just looking at the callstack in the browsers debuggers (Chrome and IE) – Aran Mulholland Nov 09 '11 at 00:07
  • if you are saying they generate a new call stack then is this guy wrong by saying it might have to still wait in the callback queue? youtu.be/8aGhZQkoFbQ?t=782 – PositiveGuy Apr 24 '16 at 03:33
  • Call stacks and callback (task) queues are completely unrelated concepts. – Domenic Apr 24 '16 at 21:38
0

There's another way to clear the call stack without using setTimeout():

testLoop.js

let counter = 0;
const max = 1000000000n  // 'n' signifies BigInteger
Error.stackTraceLimit = 100;

const A = () => {
  fp = B;
}

const B = () => {
  fp = A;
}

let fp = B;

const then = process.hrtime.bigint();

loop:   // label is not needed for this simple case though useful for some cases
for(;;) {
  counter++;
  if (counter > max) {
    const now = process.hrtime.bigint();
    const nanos = now - then;

    console.log({ "runtime(sec)": Number(nanos) / (1000000000.0) })
    throw Error('exit')
  }
  fp()
  continue loop;
}

output:

$ node testLoop.js
{ 'runtime(sec)': 20.647399601 }
C:\Users\jlowe\Documents\Projects\clearStack\testLoop.js:25
    throw Error('exit')
    ^

Error: exit
    at Object.<anonymous> (C:\Users\jlowe\Documents\Projects\clearStack\testLoop.js:25:11)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
Jeff Lowery
  • 2,492
  • 2
  • 32
  • 40