google script (JS) - maximum recursion depth - javascript

What is the maximum recursion depth in Google Apps Script scripts? I have a function, match_recurse, which looks like the following pseudocode:
function match_recurse(array) {
for (i=0, i<3, i++) {
var arr2 = array.copy().push(i);
if (is_done(arr2)) break;
match_recurse(arr2);
}
}
(It also returns its results, but I don't want to bloat the question.)
Now, because the execution errored, the execution transcript and the logs were not saved, so I have no way of knowing whether my is_done function is doing its job wrong. I can do a few cases of the problem on paper and check recursion depth, but I don't know what the maximum is supposed to be.
Looking on the web, I saw an article mentioning that IE has a max call stack of 13 if you go through the Window object, but nothing else.

It is 1000, as one can see from here:
function recurse(i) {
var i = i || 1;
try {
recurse(i+1);
} catch (e) {
Logger.log(i);
}
}

The stack depth value is not documented. Executing the following code shows that this value is equal to 1000.
function getStackDepth(curvalue) {
try {
curvalue = getStackDepth(curvalue) + 1;
}
catch(err) {
}
return curvalue;
}
function test() {
var depth = getStackDepth(2);
debugger;
}

Related

javascript recursive stack overflow

Is there anybody would explain why the result is different below?
// test one
function computeMaxCallStackSize() {
try {
return computeMaxCallStackSize() + 1;
} catch (e) {
return 1;
}
}
console.log(computeMaxCallStackSize());
result is 17958
// test two
function computeMaxCallStackSize() {
try {
return 1 + computeMaxCallStackSize();
} catch (e) {
return 1;
}
}
console.log(computeMaxCallStackSize());
result is 15714
When the position of the function 'computeMaxCallStackSize' is different,the result is different too. What's the reason? Thanks very much!
Running environment:
node.js v6.9.1
OS:Win7
Its not the position but the order of execution which leads to this in the first function the statement
return computeMaxCallStackSize() + 1;
calls the increment first and then adds 1
return 1 + computeMaxCallStackSize();
If you try both return statements as same then it leads to same value. In later one as digit is first the js call stack exceeds overflow sooner as compared to first. The callstack value depends on the order of execution as in 2nd you change the order you get a lower value as recursion happens later.
You can also check by adding some console.log() or local variable call stack will decrease gradually with increase in execution statements.
If you try computeMaxCallStackSize() + 1; in both you will get same value.

Javascript recursivity check

Is there a built-in way to check if a javascript function has been called recursively (directly or indirectly)?
By recursively, I mean that the function could be anywhere in the recursion chain (the function doesn't have to be the direct invoker).
EDIT
Obviously, there's no built-in way to achieve what I want to do. I though the plan B would be easy to implement so I came up with this solution (similar to Paolo answer):
function checkRecursion() {
var f = arguments.callee.caller;
var caller = f;
while (caller) {
caller = caller.caller;
if (caller === f) {
return true;
}
}
return false;
};
This function is working perfectly if you try to search the first recursive level.
function fA() {
if (checkRecursion()) {
alert("End of recursion");
}
else {
fB();
}
}
function fB() {
fA();
}
fA();
However, if you need to perform the same check on a function executed after an other recursion, you'll end up into an infinite loop:
var count = 0;
function fA() {
if (checkRecursion()) {
//I should get here but I get stuck in the checkRecursion()
alert("End of recursion");
}
else {
fB();
}
}
function fB() {
if (count > 2) {
fA();
} else
{
count++;
fC();
}
}
function fC() {
fB();
}
fA();
For some reason, the fB caller is fC, and the fC caller is fB so I can't travel back to the fA function as a caller of fB. This issue is way more complicated than I though it would be.
If you reflect on it, what you are getting makes perfect sense because every function in your code is a single object.
caller is a reference to that object and the latter is always the same and so they are its instance variables' values.
In other words, if a function is present more than once in the stack trace then it's "caller" appears to be always the most recent caller. That's why your function fails to go up the stack trace and ends in a infinite loop in the above case.
Let's make an example:
a => b => c => d => b => e => checkRecursion
The above is the stack trace that ends in checkRecursion at the deepest level.
b is called the first time from a the second time from d.
But what you get from caller is that b is always called from d !
It cannot have two callers because the object/function is always the same.
Climbing the stack trace you get
e <= b <= d <= c <= b <= d <= c <= b <= d...
You never reach a
To achieve what you need you may obtain the stack trace with another technique that, unfortunately, is not cross browser.
Below there is a modified version of checkRecursion that uses that technique. I tested it on Firefox, it doesn't work on Safari.
// returns true if the caller appears more than once
// in the stack trace
function checkRecursion()
{
// the name of the calling function
var fname = checkRecursion.arguments.callee.caller.name;
// obtain the stack trace ***not cross browser***
// tested on Firefox
var err = new Error();
var stack = err.stack.split('\n');
// returns true if the calling function appears more than once
var i,n,cnt;
n = stack.length;
cnt = 0;
for( i = 0; i < n; i++ )
{
if( fname == stack[i].substr(0,stack[i].indexOf('#')) )
{
cnt++;
if( cnt > 1 )
{
return true;
}
}
}
return false;
}
You can use the Chrome Developer Tool (if you use chrome) and start a new "Timeline" audit.
If you filter the result to show only the functions, you should be able to find some functions called by time.
Another thing to try is a "profile" audit to collect JavaScript CPU Profile. If you see a function name with an high percentage, that means it's using more resources and it may also be called multiple times.
Using Function.caller, you can check which function invoked your function. Check it against the last caller to see if they're the same function.
var y = function() {
console.log(this.lastCaller === y.caller);
this.lastCaller = y.caller;
}
var x = function(i) {
y();
if(i) {
x(--i);
}
}
x(3);
Outputs:
false, true, true, true

Javascript (jQuery) if statement rewrite

I'm writing some JavaScript (with jQuery) that needs to be more flexible. However, I'm not sure how to approach this. This is my code:
var scrollTop = $(this).scrollTop();
var scrollArr = $('.data').html().split(","); // eg. array is 0,80,240,350,380,630
function doIt() {
if (scrollTop<parseInt(scrollArr[0])) {
//$('.scrollShow img').eq(0).removeClass('h')
}
if (scrollTop>parseInt(scrollArr[0])&&scrollTop<parseInt(scrollArr[1])) {
$('.scrollShow .anim').eq(0).addClass('o01s')
}
if (scrollTop>parseInt(scrollArr[1])&&scrollTop<parseInt(scrollArr[2])) {
$('.scrollShow .anim').eq(1).addClass('o01s')
}
if (scrollTop>parseInt(scrollArr[2])&&scrollTop<parseInt(scrollArr[3])) {
$('.scrollShow .anim').eq(2).addClass('o01s')
}
}
The problem is that the amount of elements in the array (scrollArr) will vary each time the doIt() function is run, so if/else statements are not practical in my opinion. What should I replace them with? The code to be executed would be the same (as the above example - with the number increment) and so a flexible function seems more appropriate. Or am I going about this the wrong way? Any input would be appreciated.
for (var i=0; i<scrollArr.length; i++) {
if(scrollTop>parseInt(scrollArr[i])&&scrollTop<parseInt(scrollArr[i+1]))
{
$('.scrollShow .anim').eq(i).addClass('o01s')
}
}

How to use setTimeout in a recursion to avoid browser from getting stuck on complicated processes

I have this code that uses an inefficientProcess() that consumes plenty of memory:
My goal is to use some sort of setTimeout(function(){...},0) technique so the browser will not get stuck while executing the code.
How do I change the code so it will work with setTimeout?
function powerOfTwo(num) {
inefficientProcess();
if (num > 0) {
return powerOfTwo(num-1)*2;
} else {
return 1;
}
}
function inefficientProcess() {
var sum;
for (var i=0; i < 500000; i++) {
sum+=10;
}
}
powerOfTwo(1000);
My goal is ofcourse to learn how to avoid browser crush when executing heavy calculations.
Javascript is single-threaded, and all your code is blocking.
There is a new standard in HTML5, WebWorkers API, that will allow you to delegate your task to a different thread. You can then pass a callback function to be executed with the result.
https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
Simple example:
function powerOfTwo(num, callback) {
var worker = new Worker('inneficient.js');
worker.postMessage('runTask');
worker.onmessage = function(event) {
var num = event.data.result;
var pow;
if (num > 0) {
pow = Multiply(num-1)*2;
} else {
pow = 1;
}
callback(pow);
};
}
powerOfTwo(1000, function(pow){
console.log('the final result is ' + pow);
});
in inneficient.js you have something like:
//inneficient.js
function inefficientProcess() {
var sum;
for (var i=0; i < 500000; i++) {
sum+=10;
}
postMessage({ "result": sum});
}
inefficientProcess();
As was mentioned in Andre's answer, there's a new HTML5 standard that will allow you to set off a task on a different thread. Otherwise, you can call setTimeout with a time of 0 to allow the current execution path to finish (and perhaps some UI changes to render) before the inefficientProcess is called.
But whether you can use HTML5 or not, the powerOfTwo function has to be changed to be asynchronous - whoever calls it needs to provide a callback method that will be called when (a) a thread spun up via WebWorkers returns, or (b) the setTimeout method finishes.
Edited to add example:
function powerOfTwo(num, callback)
{
setTimeout(function ()
{
inefficientProcess();
if (num > 0)
callback(Multiply(num-1)*2);
else
callback(1);
}, 0);
}
function inefficientProcess() { ... }
The HTML element allows you to define when the JavaScript
code in your page should start executing. The “async” and “defer”
attributes were added to WebKit early September. Firefox has been
supporting them quite a while already.
Saw that on this Site

Using Javascript closure to pass additional data to an API callback function

I have gotten this to work a bit but can't seem to figure out what the exact phrasing to get the for loop to pass the correct value will be.
for(i=0; i < the_stores.length; i++) {
uid = the_stores[i].id;
// get the lat long and write info to the console
geocoder.getLatLng(the_stores[i].address, function(point) { return callbackFunc(point, uid); });
}
function callbackFunc(point, myID) {
if(point !== null) {
console.log(point.toString());
console.log(myID);
} else {
return false;
}
}
I'm using the Google Maps API, v2. I want that for loop to pass in a different value of uid to the callback function for each iteration through the loop, but right now it passes in the same value every time. Anyone know how to get the desired effect here? I feel like I'm pretty close. Thank you!
What you are looking for is an "Immediately Invoked Function Expression". Since JavaScript only has function scope, not block scope, all references to uid are being bound to the same variable.
(For more, see: http://benalman.com/news/2010/11/immediately-invoked-function-expression/)
Here's a simple way to do it:
for(i=0; i < the_stores.length; i++) {
(function() {
var uid = the_stores[i].id;
// get the lat long and write info to the console
geocoder.getLatLng(the_stores[i].address, function(point) { return callbackFunc(point, uid); });
})();
}
Use your callbackFunc to return a handler that references the values that need to be retained.
for(i=0; i < the_stores.length; i++) {
// get the lat long and write info to the console
geocoder.getLatLng(the_stores[i].address, callbackFunc(the_stores[i].id));
}
function callbackFunc(myID) {
return function(point) {
if(point !== null) {
console.log(point.toString());
console.log(myID);
} else {
return false;
}
};
}
This is why I like Array.forEach() so much. It takes care of all these kind of common problems with loops for you. Use it like this:
the_stores.forEach(function (store, i) {
geocoder.getLatLng(store.address, function(point) {
return callbackFunc(point, store.id);
});
}
Array.forEach() is native JavaScript in modern browsers, but for IE8 and IE7 you need to add it yourself. I recommend using the implementation provided by MDN. In my humble opinion, a JS5 shim that includes at least all the new Array methods should be boiler plate for any website.

Categories