4

All,

I'm working on implementing a comet JS library. Right now I'm tracking the size of the response text and returning the new portion as chunks arrive. This provides my callback the new data, but is a very obvious memory leak. Is there a way to force close an XMLHttpRequest object or to reset the contents of responseText periodically?

request.multi = function(type, handler, url, querystring){
    querystring  = (querystring == undefined) ? null: querystring;

    var response = "";
    var handle   = makeRequestHandle();

    handle.multipart = true;
    handle.open(type, url, true);
    handle.onreadystatechange = function(){
        var return_val;
        if(handle.readyState == 4){
            m_log.debug("Conection died");
        }else if(handle.readyState == 3){
            return_val = handle.responseText.substring(response.length);
            response   = handle.responseText;
            handler(return_val);
        }else{
            m_log.debug("readyState %s", handle.readyState);
        }
    };
    handle.send(querystring);
}
Goldfish
  • 73
  • 1
  • 6

1 Answers1

2

Setting the field won't help

You are correct that as long as the request is active, and that your handler consumes it, you are accumulating data somewhere in memory.

I say 'somewhere', because there would be several software layers through which the socket data passes. As long as your XHR is ongoing, your runtime (browser, or js runtime), will keep receiving. It has to perform decoding for the content (e.g. (un)gzip), and possibly decode the transfer encoding (likely to be 'chunked' if it's a long-running connection).

Regardless of if you access or set the property, there is an underlying growing buffer containing the (growing) payload.

On Chrome at least, XMLHttpRequest.prototype.responseText is defined as a getter property only. Setting it (e.g. with xhr.responseText = '' will have no effect on the underlying buffer. Memory will keep growing

// you can verify
var xhr = new XMLHttpRequest();
Console.log(Object.getOwnPropertyDescriptor(x.__proto__, "response"))

// I get
{... get: function () {...}, set: undefined }

The same goes for the other "views" that you may have on the xhr payload, xhr.response (for blobs) and xhr.responseXML.

Do the tango with a second XHR

The most reliable way to have your connection memory "reset", without losing messages, is to start a new ajax request while the old one is still active, and switch to the new one after the old one receives its full last message. While both are active, one would have to ignore data on the new stream until a point where they are in 'sync'.

Memory efficient message chunk processing using a XMLHttpRequest

This of course assumes that the data received on the streams would be the same, or that there is some way to uniquely identify messages coming in to make the two streams "align".

multipart/x-mixed-replace

There used to be such a content-type you could set, which would instruct the client to reset its response whenever a particular string was received.

It was designed for streaming webcam images, got abused for a while with comet, and sort of does what you want, but its support was dropped at least from Firefox and Chrome. It was famously known to not be supported by IE from the start, so that's probably true today too.

Content-type: multipart/x-mixed-replace;boundary=<randomstringhere>

Where <randomstringhere> is something that is unlikely to appear in your application messages. Between each message "push" that the server makes, you insert <randomstringhere>. Browsers would reset the responseText at each boundary automatically.

One problem with that one, is that you'd have to have processed all the messages in the boundary before the browser received the next one. So it was racy, for AJAX at least.

Simplify your life with x.response

In the example given in the question, responseText is used to get the full payload. Before the send() call, one may set handle.responseType = "text", and then in the progress callback, handle.response would carry only the information that is new since the last time the handler was called.

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/response

Chances are, however, that the whole payload is still made available via responseText, so that wouldn't be enough to fix the memory runaway problem.

Community
  • 1
  • 1
init_js
  • 4,143
  • 2
  • 23
  • 53