title: Cross-browser implementation of "HTTP Streaming" (push) AJAX pattern
body: <p>Client request web page from server. Clent then requests for extra calculations to be done; server performs series of calculations and sends partial results as soon as they are available (text format, each line contains separate full item). Client updates web page (with JavaScript and DOM) using information provided by server.</p>
  
  <p>This seems to fit <a href="http://ajaxpatterns.org/archive/HTTP_Streaming.php" rel="noreferrer">HTTP Streaming</a> (<a href="http://ajaxpatterns.org/HTTP_Streaming" rel="noreferrer">current</a> version) pattern from Ajaxpatterns site.</p>
  
  <p>The question is how to do it in cross-browser (browser agnostic) way, preferably without using JavaScript frameworks, or using some lightweight framework like jQuery. </p>
  
  <p>The problem begins with generating XMLHttpRequest in cross-browser fashion, but I think the main item is that not all browsers implement correctly <code>onreadystatechange</code>from <a href="http://www.w3.org/TR/XMLHttpRequest/" rel="noreferrer">XMLHttpRequest</a>; not all browsers call <code>onreadystatechange</code> event on each server flush (BTW. how to force server flush from within CGI script (in Perl)?). Example code on Ajaxpatterns deals with this by using timer; should I drop timer solution if I detect partial response from <code>onreadystatechange</code>?</p>
  
  <hr>
  
  <p><strong><em>Added 11-08-2009</em></strong></p>
  
  <p><strong>Current solution:</strong><br>
  I use the following function to create XMLHttpRequest object:</p>
  
  <pre><code>function createRequestObject() {
          var ro;
          if (window.XMLHttpRequest) {
                  ro = new XMLHttpRequest();
          } else {
                  ro = new ActiveXObject("Microsoft.XMLHTTP");
          }
          if (!ro)
                  debug("Couldn't start XMLHttpRequest object");
          return ro;
  }
  </code></pre>
  
  <p>If I were to use some (preferably light-weight) JavaScript framework like jQuery, I'd like to have fallback if user chooses not to install jQuery.</p>
  
  <p>I use the following code to start AJAX; <code>setInterval</code> is used because some browsers call <code>onreadystatechange</code> only after server closes connection (which can take as long as tens of seconds), and not as soon as server flushes data (around every second or more often).</p>
  
  <pre><code>function startProcess(dataUrl) {
          http = createRequestObject();
          http.open('get', dataUrl);
          http.onreadystatechange = handleResponse;
          http.send(null);
  
          pollTimer = setInterval(handleResponse, 1000);
  }
  </code></pre>
  
  <p>The <code>handleResponse</code> function is most complicated one, but the sketch of it looks like the following. Can it be done better? How it would be done using some lightweight JavaScript framework (like jQuery)?</p>
  
  <pre><code>function handleResponse() {
      if (http.readyState != 4 &amp;&amp; http.readyState != 3)
          return;
      if (http.readyState == 3 &amp;&amp; http.status != 200)
          return;
      if (http.readyState == 4 &amp;&amp; http.status != 200) {
          clearInterval(pollTimer);
          inProgress = false;
      }
      // In konqueror http.responseText is sometimes null here...
      if (http.responseText === null)
          return;
  
      while (prevDataLength != http.responseText.length) {
          if (http.readyState == 4  &amp;&amp; prevDataLength == http.responseText.length)
              break;
          prevDataLength = http.responseText.length;
          var response = http.responseText.substring(nextLine);
          var lines = response.split('\n');
          nextLine = nextLine + response.lastIndexOf('\n') + 1;
          if (response[response.length-1] != '\n')
              lines.pop();
  
          for (var i = 0; i &lt; lines.length; i++) {
              // ...
          }
      }
  
      if (http.readyState == 4 &amp;&amp; prevDataLength == http.responseText.length)
          clearInterval(pollTimer);
  
      inProgress = false;
  }
  </code></pre>