views:

440

answers:

3

I'm writing a webapp (Firefox-compatible only) which uses long polling (via jQuery's ajax abilities) to send more-or-less constant updates from the server to the client. I'm concerned about the effects of leaving this running for long periods of time, say, all day or overnight. The basic code skeleton is this:

function processResults(xml)
{
    // do stuff with the xml from the server
}

function fetch()
{
    setTimeout(function ()
    {
        $.ajax({
            type: 'GET',
            url: 'foo/bar/baz',
            dataType: 'xml',
            success: function (xml)
            {
                processResults(xml);
                fetch();
            },
            error: function (xhr, type, exception)
            {
                if (xhr.status === 0)
                {
                console.log('XMLHttpRequest cancelled');
                }
                else
                {
                    console.debug(xhr);
                    fetch();
                }
            }
        });
    }, 500);
}

(The half-second "sleep" is so that the client doesn't hammer the server if the updates are coming back to the client quickly - which they usually are.)

After leaving this running overnight, it tends to make Firefox crawl. I'd been thinking that this could be partially caused by a large stack depth since I've basically written an infinitely recursive function. However, if I use Firebug and throw a breakpoint into fetch, it looks like this is not the case. The stack that Firebug shows me is only about 4 or 5 frames deep, even after an hour.

One of the solutions I'm considering is changing my recursive function to an iterative one, but I can't figure out how I would insert the delay in between Ajax requests without spinning. I've looked at the JS 1.7 "yield" keyword but I can't quite wrap my head around it, to figure out if it's what I need here.

Is the best solution just to do a hard refresh on the page periodically, say, once every hour? Is there a better/leaner long-polling design pattern that won't put a hurt on the browser even after running for 8 or 12 hours? Or should I just skip the long polling altogether and use a different "constant update" pattern since I usually know how frequently the server will have a response for me?

+1  A: 

I suspect that memory is leaking from processResults().

I have been using very similar code to yours in a long-polling web application, which is able to run uninterrupted for weeks without a page refresh.

Your stack should not be deep, because fetch() returns immediately. You do not have an infinitely recursive loop.

You may want to use the Firefox Leak Monitor Add-on to assist you in finding memory leaks.

Daniel Vassallo
I thought of that as another way to "break" the stack depth but I don't see that having any different effect from the `setTimeout` in my original code.
Matt Ball
Yes, however your stack should not be going deep, because fetch() returns immediately, so the success and error functions go out of the stack pretty quickly.
Daniel Vassallo
I suspect that your memory is leaking from `processResults()`.
Daniel Vassallo
It's possible. What's the best tool for detecting memory leaks in FF?
Matt Ball
You may want to use the Leak Monitor Addon: https://addons.mozilla.org/de/firefox/addon/2490. The topic was also discussed here: http://stackoverflow.com/questions/200822/how-do-i-track-and-debug-javascript-memory-leaks-in-firefox
Daniel Vassallo
I just realized that I haven't tried running it for a long time with Firebug disabled. That could very well be the problem.
Matt Ball
@Bears: It could be it, but I still manage to keep my long-polling application with Firebug open for several days without a page refresh, and without apparent memory leaks. However firebug might be leaking out memory because of something legitimate in `processResults()`, which my application is not doing. So yes, it might be the cause of your problem.
Daniel Vassallo
A: 

The stack depth of 4-5 is correct. setTimeout and $.ajax are asynchronous calls, which return immediately. The callback is later called by the browser with an empty call stack. Since you cannot implement long polling in a synchronous way, you must use this recursive approach. There is no way to make it iterative.

I suspect the reason for this slow down is that your code has a memory leak. The leak could either be in $.ajax by jQuery (very unlikely) or in your processResults call.

Fabian Jakobs
A: 

It's also possible that it's FireBug. You're console.logging stuff, which means you probably have a network monitor tab open, etc, which means every request is stored in memory.

Try disabling it, see if that helps.

jvenema