简体   繁体   中英

jquery setTimeout too much recursion

I have read from multiple places that setTimeout() is preferable to setInterval() when setting something up to basically run forever. The code below works fine but after about an hour of running Firefox (38.0.1) throws an error of too much recursion .

Essentially I have it grabbing a very small amount of text from counts.php and updating a table with that information. The whole call and return takes about 50ms according to the inspectors. I'm trying to have it do this every x seconds as directed by t .

I suspect if I switch to setInterval() this would probably work, but I wasn't sure what the current state of the setTimeout() vs setInterval() mindset is as everything I've been finding is about 3-5 years old.

$(document).ready(function() {
    t = 3000;
    $.ajaxSetup({cache: false});

     function countsTimer(t) {
        setTimeout(function () {
            $.getJSON("counts.php", function (r) {
                $(".count").each(function(i,v) {
                    if ($(this).html() != r[i]) {
                        $(this).fadeOut(function () {
                            $(this)
                                .css("color", ($(this).html() < r[i]) ? "green" : "red")
                                .html(r[i])
                                .fadeIn()
                                .animate({color: '#585858'}, 10000);
                        })
                    };
                });

                t = $(".selected").html().slice(0,-1) * ($(".selected").html().slice(-1) == "s" ? 1000 : 60000);

                countsTimer(t);
            });
        }, t);
    };
    countsTimer(t);
});

Update: This issue was resolved by adding the .stop(true, true) before the .fadeOut() animation. This issue only occurred in Firefox as testing in other browsers didn't cause any issues. I have marked the answer as correct in spite of it not being the solution in this particular case but rather it offers a good explanation in a more general sense.

You should indeed switch to setInterval() in this case. The problem with setInterval() is that you either have to keep a reference if you ever want to clear the timeout and in case the operation (possibly) takes longer to perform than the timeout itself the operation could be running twice.

For example if you have a function running every 1s using setInterval, however the function itself takes 2s to complete due to a slow XHR request, that function will be running twice at the same time at some point. This is often undesirable. By using setTimout and calling that at the end of the original function the function never overlaps and the timeout you set is always the time between two function calls.

However, in your case you have a long-running application it seems, because your function runs every 3 seconds, the function call stack will increase by one every three seconds. This cannot be avoided unless you break this recursion loop. For example, you could only do the request when receiving a browser event like click on the document and checking for the time.

(function() 
{
    var lastCheck = Date.now(), alreadyRunning = false;
    document.addEventListener
    (
        "click", 
        function() 
        {
            if(!alreadyRunning && Date.now() - lastCheck > 3000) 
            {
                alreadyRunning = true;
                /* Do your request here! */
                //Code below should run after your request has finished
                lastCheck = Date.now();
                alreadyRunning = false;
            }
        }
    )
}());

This doesn't have the drawback setInterval does, because you always check if the code is already running, however the check only runs when receiving a browser event. (Which is normally not a problem.) And this method causes a lot more boilerplate.

So if you're sure the XHR request won't take longer than 3s to complete, just use setInterval().

Edit: Answer above is wrong in some aspects

As pointed out in the comments, setTimeout() does indeed not increase the call stack size, since it returns before the function in the timeout is called. Also the function in the question does not contain any specific recursion. I'll keep this answer because part of the question are about setTimeout() vs setInterval(). However, the problem causing the recursion error will probably be in some other piece of code since there is not function calling itself, directly or indirectly, anywhere in the sample code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM