Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 419
The_Black_Smurf Avatar asked Sep 06 '26 06:09

The_Black_Smurf


1 Answers

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;
}
like image 188
Paolo Avatar answered Sep 07 '26 21:09

Paolo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!