Reputation: 5269
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.
Upvotes: 3
Views: 1506
Reputation: 15827
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;
}
Upvotes: 1
Reputation: 2741
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
Upvotes: 0
Reputation: 863
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.
Upvotes: 0