I have this function which I want to loop without stopping the script.
But when I use 'console.log' the function stops. If I remove it, the function continues.
How to make this function continue after it has exceeded stack size?
var i = 0;
function computeMaxCallStackSize() {
try {
i++;
console.log(i);
//some function to execute
computeMaxCallStackSize();
} catch (e) {
//'e' is the error when max stack exceeds
// setTimeout() is to wait before calling the function
// if stack size exceeds it recalls the function
setTimeout(computeMaxCallStackSize(), 0);
}
}
computeMaxCallStackSize();
Edit:
This function will cause a stack overflow and continues.
var a = 1;
var b = 8769; //stack size
func(a);
function func(a) {
try {
console.log(a);
if(a % b === 0){
setTimeout( function() {func(a+1); }, 0);
} else {
func(a+1);
}
} catch (e) {
console.error(e);
setTimeout( function() {func(a); }, 1);
}
}
If I remove it, the function continues.
No it doesn't, it will finish also, but much later. console.log() taking much more resources cause it needs to render text in console, so it's reaching call stack faster.
You just made infinite loop, that will run till browser will run out of memory.
I don't see a way to catch stackoverflow in nodejs. From what I know from other ecosystems, it is impossible. See C# catch a stack overflow exception for example. Also see a note about exceptions:
Some exceptions are unrecoverable at the JavaScript layer. Such
exceptions will always cause the Node.js process to crash. Examples
include assert() checks or abort() calls in the C++ layer.
Process stack is fixed-size, contiguous block of memory, usually not very big (because it's purpose is to make use of process caches). This means that you can't "extend" it on your wish (only at process start). If you can't extend it, why bothering checking it's boundaries every time you do a function call? OS will do the job and crash your process for you!
I know it's not always possible in case of highly-recursive academic algorithms, but from business perpective, you should always unwrap your recursion, especially if it's deep. Even if you think you control it (like, it's only 2000 deep, who cares?), in reality you don't, since usually you don't fully control the caller stack size, and the end-user platform limitations.
BUT!
If you REALLY want to recover your process after stackoverflow, and you're ready for such an adventure (win api, build node from sources, etc.), I will leave this for you.
Your setTimeout is kind of pointless the way it is:
// Current code:
setTimeout(computeMaxCallStackSize(), 0);
// What you really want:
setTimeout(computeMaxCallStackSize, 0);
To answer your question - you don't want to write code which can exceed the stack - that's always an indication of poorly written code. Furthermore, there's not really a way to check the stack size or to check when the stack is "clear" again (for good reason - people would abuse that knowledge to write poor code).
If you need recursion, you almost always build in a "depth" parameter to the function so the function stops after a certain call depth. Something like this:
function someRecursiveFunction(_depth = 0) {
if(_depth < 100) {
// do the stuff
someRecursiveFunction(_depth + 1)
}
throw new Error('Oh no, we called this function too many times');
}
If you're just doing this for giggles and really want to see how deep the stack hole goes, I guess you could count the iterations. As mentioned in other comments, the console log is going to take up additional resources - so you might get 1 million iterations without the logging and only 100K with logging. Again, there's no way to ask the JS runtime "how many resources to I have left". The function you are writing is simply a "for fun" experiment and should not be used in real life code.
Related
I was writing some code for a widget in "Scriptable" that shows random word at a certain time. The widget calls itself with a timeout, on iPad. But it's exceeding the stack size. How do I solve this? I am using Javascript within "Scriptable" framework, so I don't have much freedom.
kanjiObj: Object; kanjiKeys: List; Timer().schedule(timeinterval, repeat, callback)
var randomNum = Math.floor(Math.random()*150)+1
var kanji = kanjiKeys[randomNum]
var kanjiMeaning = kanjiObj[kanjiKeys[randomNum]]
if(config.runsInWidget){
let widget = createWidget()
Script.setWidget(widget)
Script.complete()
function createWidget(){
let widget = new ListWidget()
widget.addText(kanji + "=>" + kanjiMeaning)
widget.wait = new Timer().schedule(1000*60*60, 150, createWidget())
In your case, you are calling the createWidget() function recursively by mistake. Your intention was different, you wanted to pass the function as a parameter to the schedule function, which accepts a function callback as the last parameter.
So you have to change to
widget.wait = new Timer().schedule(1000*60*60, 150, () => createWidget())
or
widget.wait = new Timer().schedule(1000*60*60, 150, createWidget)
But since this question is about "call stack size exceeded" people who have a different kind of this problem could stumble upon it, so I will answer in a general way below as well.
Programming is about recognizing patterns, so what you have in your code is a pattern (structure) of an infinite recursive call
function someFunction() {
// ... code before the recursive call that never returns from this function
// an unconditional call (or a conditional chain that is always true)
// of the function itself in the function scope instead of inside a callback/lambda
someFunction()
// ... could be more code, which does not matter,
// until the pattern above still holds true
}
So a structure (pattern) like this will result in an infinite recursive call, which eventually ends with a maximum call stack size exceeded. Call stack size is limited and an infinite recursive call eventually fills it up.
Javascript does not have built-in tail-recursion optimization (in practice, see compatibility), but in programming languages that do, if the function is tail-recursive, it would be optimized to a loop. In the case of an infinite tail-recursive function it would be optimized to an infinite loop, which would not fill the call stack, it would just loop forever (it would be working, but if not implemented correctly, it could make the application unresponsive).
So to resolve a problem like this in general, one has to break this pattern by breaking any (at least one) of the conditions above. This is the list:
code before the recursive call that never returns from this function
add a conditional return statement (also called a stop condition)
an unconditional call (or a conditional chain that is always true) to the function itself in the function scope instead of inside a callback/lambda
make the call conditional (make sure the condition chain can be false) or put it inside a callback/lambda. When you put it inside a
callback/lambda, then a different pattern applies (you have to check
the logic inside the call that will be calling the callback), but
calling the callback still has to be conditional, it has to have a
limit at some point.
after making a change, the code that is after the call, needs to be checked for the same pattern again, if the pattern is there again, break it in a similar way. Keep repeating this until the whole function does not form the pattern anymore - has stop condition(s) where needed.
In cases when you do need an infinite recursive call and the function is tail-recursive and your language does not support tail-recursion optimization, you would need to do the tail optimization yourself or use a library/framework that lets you do that.
If this does not solve your problem, have a look at the answers in this question that has collected a lot of reasons why a "maximum call stack size exceeded" might happen in Javascript.
When I'm am executing the following code in Node.js, I get the folliowing error:
RangeError: Maximum call stack size exceeded
This is the code:
var arr = [];
for (var i = 0; i <= 1000000; i++)
arr.push(i);
function nextStep(index) {
if (index === arr.length) {
return;
} else {
console.log(index);
}
nextStep(++index);
}
nextStep(0);
I have no clue what is happening, but near index = 17938, the execution terminates.
Using a setTimeout() helps. What could be wrong here?
You are entering a recursive function. This means that the first function won't return until all the other functions have returned. So if you had four items,
fn(item1)
calls -> fn(item2)
calls -> fn(item3)
calls -> fn(item4)
As you can see, the nesting builds up and up. This is called the stack. There is a maximum size for the stack, to prevent infinite recursion and runaway processes. You have found it with 17938.
This is an inherent flaw with recursion. It can be a stylish way to approach a task, but it has its limitations. The best way to correct it is to use a loop instead:
for (var i = 0; i < arr.length; i++) {
Using setTimeout also works, because the function is not called from the function itself, but is executed with a new stack. However, it will have significantly lower performance than a loop or than a normal recursive function.
There's a certain number of calls that can be made, based on the call stack of different browsers. Most likely, you are testing your code in Chrome, since, I believe, it has the call stack near 20.000. Your code will execute the nextStep function more than 20.000 times (1 million), which means that if your function will not return something until it reaches the call stack limit of that particular browser, it will generate the error that you are getting.
Calling nextStep inside nextStep causes a stack overflow since you never return from the function (unless you found the end of the array, and if the array is too big you will never reach it until the stack overflows).
An example:
You are going to move all the stones from one place to another. Your function is like going to the place where the stones are and pick up one to be delivered to another place. But before you can deliver the stone, you must pick up another, and before you can deliver that you need to pick up another... and soon you are carrying 17938 stones. That is a little bit to heavy and you get crushed under all the stones. (or in the javascript case you get an exception)
When you use setTimetout it is like you are going to the place, pick up a stone, and make a note that you should pickup another stone. Then you deliver the stone. After that you look at your notes and see that you should pick up at stone. And you can do this 1000000 times since you are only carrying one stone at a time.
So, we all know recursive functions, right? But what exactly is it, that makes a function recursive? I had a small discussion in the comment section of another question (Lightening effect with javascript) that kind of challenged my view of recursive functions but it also left me with a quite unsatisfying lack of proper definition.
My personal definition of recursive functions so far was the following:
A function is recursive if it directly or indirectly invokes itself
Note: I will provide JavaScript code for the following examples, but I'm sure they are pretty universal.
A simple example for such a recursive function could be this:
function a() {
a();
}
but also this:
function a() {
b();
}
function b() {
a();
}
or even this:
function a() {
setTimeout(a, 1000);
}
None of those functions computes anything but I would have still considered them recursive just because they invoke themselves.
One thing that came up was, that the third example is not recursive because it uses setTimeout and therefor the stack is unwound. It also isn't recursive because after returning from the nth invocation it doesn't give back control to the n-1th invocation.
Another point, that was raised, was that none of those functions were recursive because they all don't compute an actual problem in a recursive way. Meaning that a problem has to be solved by dividing it in smaller and smaller instances. Here the quoted Wikipedia article:
Recursion in computer programming is exemplified when a function is
defined in terms of simpler, often smaller versions of itself. The
solution to the problem is then devised by combining the solutions
obtained from the simpler versions of the problem.
So this is recursive:
function fac(n) {
if (n <= 0) {
return 1;
}
return n * fac(n - 1);
}
But this wouldn't be:
function fac(n) {
if (n <= 0) {
return 1;
}
console.log(n);
fac(n - 1);
}
So what is the proper definition of a recursive function? Is there any at all or is it really just a philosophical question? What features must a function have, in order to be classified recursive?
Recursion is simply defining a problem in terms of a simpler case (simpler meaning "closer" to the terminating condition, not necessarily actually simpler) of that problem, down to the point where the simplest case is a known one (the terminating condition alluded to earlier). So, for example, the perennial factorial function has a terminating condition:
f(1) = 1
and the definition of the problem in terms of a simpler one:
f(n) = n * f(n - 1), for n > 1
The best explanation of it that I ever heard was this:
If you're Donald Knuth, you understand what it is.
Otherwise, find someone closer to Donald and ask them.
I wouldn't call the setTimeout one recursion because a is not actually calling itself. Instead, it's asking the "system" to call it at some later date.
It's also important to have the terminating condition in there somewhere. Without that, it's still recursion but it's infinite recursion, no different to infinite loops like:
for (i = 0; i < 10; j++) {}
and hence unlikely to be any good for anything other than testing what happens when your stack overflows :-)
Definition of Recursion? Read this line again until you get it.
(A selfcalling function with an abort criterium to prevent infinite looping).
Recursion is the name that was given to function that calls itself. Now whether the function calls itself infinitely or not.. it is still a Recursion.
It is not necessarily that the problem is divided into sub-problems. However, in computer science; The term Recursion refers to a technique that is used to solve a problem, by breaking the problem into sub-problems and usually the problem is finite.
One more point, Recursion is implemented using Stack. Each function call is piled on top of the other in the stack, until the last call meets the base condition, then the functions in the stack are executed from top to bottom.
However, if there is no base condition or the base condition is never to be satisfied. then infinite calls to the function will be pushed to the stack causing the memory to be filled up and a stackOverFlow exception will be thrown and OS handles it by terminating the program.
In Regard to setTimeout() It is an asynchronous call and is not related to recursion, it is an independent call as the caller function doesn't depend on the called one whether it is the same function being called or another.
From Wikipedia that you have posted:
Recursion in computer programming is exemplified when a function is defined in terms of simpler, often smaller versions of itself. The solution to the problem is then devised by combining the solutions obtained from the simpler versions of the problem.
So. There is a problem, and there is a solution. There is a function that call itself minimizing the main problem. This function is recursive.
Case:
function a() {
a();
}
there is no problem, there is nothing to minimize, there is no solution. It's not recursive for me. It's just an infinite loop.
So another example:
function a(n) {
if(n<.5) {
return n+a(Math.random());
}else {
return n;
}
}
console.log(a(.3));
Is this recursive?
No.
There is maybe a problem, but the solution is not found minimzing the main problem. Simple it call itself randomly until some flag is true. Again, it's a loop.
The same happens with a setTimeout or setInterval (the solution of problem will depend from system call).
var recurse = function(steps, data, delay) {
if(steps == 0) {
console.log(data.length)
} else {
setTimeout(function(){
recurse(steps - 1, data, delay);
}, delay);
}
};
var myData = "abc";
recurse(8000, myData, 1);
What troubles me with this code is that I'm passing a string on 8000 times. Does this result in any kind of memory problem?
Also, If I run this code with node.js, it prints immediately, which is not what I would expect.
If you're worried about the string being copied 8,000 times, don't be, there's only one copy of the string; what gets passed around is a reference.
The bigger question is whether the object created when you call a function (called the "variable binding object" of the "execution context") is retained, because you're creating a closure, and which has a reference to the variable object for the context and thus keeps it in memory as long as the closure is still referenced somewhere.
And the answer is: Yes, but only until the timer fires, because once it does nothing is referencing the closure anymore and so the garbage collector can reclaim them both. So you won't have 8,000 of them outstanding, just one or two. Of course, when and how the GC runs is up to the implementation.
Curiously, just earlier today we had another question on a very similar topic; see my answer there as well.
It prints immediately because the program executes "immediately". On my Intel i5 machine, the whole operation takes 0.07s, according to time node test.js.
For the memory problems, and wether this is a "cheap infinite loop", you'll just have to experiment and measure.
If you want to create an asynchronous loop in node, you could use process.nextTick. It will be faster than setTimeout(func, 1).
In general Javascript does not support tail call optimization, so writing recursive code normally runs the risk of causing a stack overflow. If you use setTimeout like this, it effectively resets the call stack, so stack overflow is no longer a problem.
Performance will be the problem though, as each call to setTimeout generally takes a fair bit of time (around 10 ms), even if you set delay to 0.
The '1' is 1 millisecond. It might as well be a for loop. 1 second is 1000. I recently wrote something similar checking on the progress of a batch of processes on the back end and set a delay of 500. Older browsers wouldn't see any real difference between 1 and about 15ms if I remember correctly. I think V8 might actually process faster than that.
I don't think garbage collection will be happening to any of the functions until the last iteration is complete but these newer generations of JS JIT compilers are a lot smarter than the ones I know more about so it's possible they'll see that nothing is really going on after the timeout and pull those params from memory.
Regardless, even if memory is reserved for every instance of those parameters, it would take a lot more than 8000 iterations to cause a problem.
One way to safeguard against potential problems with more memory intensive parameters is if you pass in an object with the params you want. Then I believe the params will just be a reference to a set place in memory.
So something like:
var recurseParams ={ steps:8000, data:"abc", delay:100 } //outside of the function
//define the function
recurse(recurseParams);
//Then inside the function reference like this:
recurseParams.steps--
I have a pretty simple thing I'm doing with javascript and basically only sometimes will javascript give me a "too much recursion" error.
The code in question:
if(pageLoad===undefined){
var pageLoad=function(){};
}
var pageLoad_uniqueid_11=pageLoad;
var pageLoad=function(){
pageLoad_uniqueid_11();
pageLoad_uniqueid_12();
};
var pageLoad_uniqueid_12=function(){
alert('pageLoad');
};
$(document).ready(function(){
pageLoad();
});
(yes I know there are better way of doing this. This is difficult to change though, especially because of ASP.Net partial postbacks which aren't shown).
Anyway, when the too much recursion error happens, it continues to happen until I restart Firefox. when I restart Firefox it all works as normal again. How do I fix this?
I've also made a jsbin example
Update
Ok I've found out how to reliably reproduce it in our code, but it doesn't work for the jsbin example. If I create a new tab and go to the same page(have two tabs of the same address) and then refresh the first tab two times then I get this error consistently. We are not using any kind of session or anything else that I can think of that could cause such a problem to only occur in one tab!
Update 2
Not as reliable as I thought, but it definitely only occurs when more than one tab of the same page is open. It'll occur every few reloads of one of the tabs open
I've also updated my code to show an alert when pageLoad(the if statement) is initially undefined and when it is initially defined. Somehow, both alerts are showing up. This code is not duplicated in the rendered page and there is no way that it is being called twice. It is in a top level script element not surrounded by a function or anything! My code ends up looking like
if(pageLoad===undefined){
var pageLoad=function(){};
alert('new');
} else {
alert('old');
}
The code in question -- by itself -- should never result in an infinite recursion issue -- there is no function-statement and all the function objects are eagerly assigned to the variables. (If pageload is first undefined it will be assigned a No-Operation function, see next section.)
I suspect there is additional code/events that is triggering the behavior. One thing that may cause it is if the script/code is triggered twice during a page lifetime. The 2nd time pageload will not be undefined and will keep the original value, which if it is the function that calls the other two functions, will lead to infinite recursion.
I would recommend cleaning up the approach -- and having any issues caused by the complications just disappear ;-) What is the desired intent?
Happy coding.
This is just some additional info for other people trying to look for similar "too much recursion" errors in their code. Looks like firefox (as an example) gets too much recursion at about 6500 stack frames deep in this example: function moose(n){if(n%100 === 0)console.log(n);moose(n+1)};moose(0) . Similar examples can see depths of between 5000 and 7000. Not sure what the determining factors are, but it seems the number of parameters in the function drastically decrease the stack frame depth at which you get a "too much recursion" error. For example, this only gets to 3100:
function moose(n,m,a,s,d,fg,g,q,w,r,t,y,u,i,d){if(n%100 === 0)console.log(n);moose(n+1)};moose(0)
If you want to get around this, you can use setTimeout to schedule iterations to continue from the scheduler (which resets the stack). This obviously only works if you don't need to return something from the call:
function recurse(n) {
if(n%100 === 0)
setTimeout(function() {
recurse(n+1)
},0)
else
recurse(n+1)
}
Proper tail calls in ECMAScript 6 will solve the problem for some cases where you do need to return something from calls like this. Until then, for cases with deep recursion, the only answers are using either iteration, or the setTimeout method I mentioned.
I came across this error. The scenario in my case was different. The culprit code was something like this (which is simple concatenation recessively)
while(row)
{
string_a .= row['name'];
}
I found that JavaScript throws error on 180th recursion. Up till 179 loop, the code runs fine.
The behaviors in Safaris is exactly the same, except that the error it shows is "RangeError: Maximum call stack size exceeded." It throws this error on 180 recursion as well.
Although this is not related to function call but it might help somebody who are stuck with it.
Afaik, this error can also appear if you state a wrong parameter for your ajax request, like
$.getJSON('get.php',{cmd:"1", elem:$('#elem')},function(data) { // ... }
Which then should be
elem:$('#elem').val()
instead.
This will also cause the "too much recursion" issue:
class account {
constructor() {
this.balance = 0; // <-- property: balance
}
set balance( amount ) { // <-- set function is the same name as the property.
this.balance = amount; // <-- AND property: balance (unintended recursion here)
}
}
var acc = new account();
Using unique names is important.
Ok, so why is this happening?
In the set function it isn't actually setting the property to amount, instead it's calling the set function again because in the scope of the set function it is the same syntax for both setting the property AND calling the set function.
Because in that scope this is the same as account and (account OR this).balance = amount can both call the set function OR set the property.
The solution to this is to simply change the name of either the property or the set function in any way (and of course update the rest of the code accordingly).