In Chrome, XmlHttpRequest.responseText is accessible during the loading phase (readyState==3)

Currently, I am attempting to implement server-to-client streaming in JavaScript using AJAX (specifically XmlHttpRequest or xhr). My approach involves utilizing a modified version of the handleResponse function as outlined in the following Cross-browser implementation of "HTTP Streaming" (push) AJAX pattern

function handleResponse() {
if (http.readyState != 4 && http.readyState != 3)
    return;
if (http.readyState == 3 && http.status != 200)
    return;
if (http.readyState == 4 && http.status != 200) {
    clearInterval(pollTimer);
    inProgress = false;
}
// In certain cases, http.responseText may be null... 
if (http.responseText === null)
    return;

while (prevDataLength != http.responseText.length) {
    if (http.readyState == 4  && 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 < lines.length; i++) {
        // ...
    }
}

if (http.readyState == 4 && prevDataLength == http.responseText.length)
    clearInterval(pollTimer);

inProgress = false;
}

I am using a PHP script to flush data, which works effectively without AJAX and provides real-time data updates to the browser.

While Firefox performs well with this setup, Google Chrome and IE present an issue where they return an empty responseText when xhr.readyState is at 3. Despite researching online, I have not found a solution to address this problem.

If anyone has insight into how to overcome this implementation issue specifically in Chrome, it would be greatly appreciated. (According to W3C standards, responseText should not be NULL in readyState==3 - Chrome deviates from this standard by providing an empty string).

In case you do not have a direct solution, are there any frameworks or libraries that successfully tackle this challenge?

Thank you in advance for your input.

Edit: One workaround involves creating an iframe, executing the script within the iframe to flush data, and then retrieving the data via JavaScript from the iframe. However, this method does not utilize AJAX. Ideally, I am seeking a pure AJAX solution.

Answer №1

In Chrome, there is a known bug that causes xhr.responseText to only populate after a certain number of bytes has been received. To work around this issue, you have two options:

One way is to set the content type of the return to "application/octet-stream".

Alternatively,

You can send a prelude of approximately 2kb to prepare the handler for the response.

Using either of these methods should prompt Chrome to fill in the responseText field when readyState equals 3.

However, IE7/8 lacks this capability and requires long polling or utilizing the cross domain trick with XDomainRequest in IE8. More information on this can be found at MS

Answer №2

Building upon the response from Andrew, I have devised a solution that is compatible across all browsers.

This method operates effectively in 99% of browsers, including IE versions 8 and above, Chrome, Firefox, and Safari. It sends incremental events as soon as data is received by the browser, with some exceptions noted below.

if (/MSIE [8-9]/.test(navigator.appVersion)) {
    var get = new XDomainRequest()
    get.onprogress = handleData
    get.onload = handleData
} else {
    var get = new XMLHttpRequest()
    get.onreadystatechange = handleData
}
get.open('get', '/example/url')
get.send()

function handleData() {
    if (get.readyState != null && (get.readyState < 3 || get.status != 200)) {
        return
    }
    // process incremental data found in get.responseText
}

In IE versions 8 and 9, responseText begins populating after receiving 2kB of data. To avoid this issue, consider sending an initial padding of 2kB.

In the case of Chrome, you may also need to include either a padding or specify

Content-Type: application/octet-stream
.

Answer №3

Have you thought about utilizing WebSockets or server-sent events for your project?

The majority of popular browsers now have support for the WebSocket protocol. However, if your website needs to be compatible with older versions like IE 9 or Android Browser 4.3 and lower, you may need to include code that utilizes XMLHttpRequest as a backup solution.

Most of these web browsers also have the capability for 'server-sent events,' which is different from WebSockets. Server-sent events can be set up on the server side using a traditional HTTP daemon and CGI/PHP script, but it only enables one-way communication.

Check out this resource: Comparison of WebSockets vs. Server-Sent Events/EventSource

Answer №4

Unfortunately, not all browsers fully implement every part of XmlHttpRequest or other web standards. However, there are alternative options for HTTP Streaming available:

In your comment, you mentioned a preference for pure AJAX, but I'd like to suggest considering other potential solutions. You could utilize a JavaApplet or a Flash Object. With the latter option, you can create Flash/SWF files using Haxe without needing an expensive IDE. Haxe may feel familiar to you if you're already comfortable with JavaScript.

For example, here's a Flash/Neko chat example that can likely be adapted for different platforms and purposes as needed.

Best of luck in your endeavors!

Answer №6

My interpretation is that the action of presenting partial text during readyState 3 is a unique feature exclusive to Firefox. It seems challenging to replicate this behavior in other browsers directly. A possible alternative approach could involve sending multiple consecutive requests for small portions of the information instead of one continuous 'streaming' request.

Answer №7

My experience with this code was that it worked well in Chrome, but not in Internet Explorer:

[test.php]:

<?php
Header('Content-type: text/plain');
while (1) {
    echo str_pad('test: '.mt_rand(1000,9999), 2048, ' ');
    flush();
    sleep(1);
}

[test.html]:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Stream test</title>
        <meta charset="UTF-8" />
        <script type="text/javascript">
            function xmlHttpRequest() {
                return (function (x,y,i) {
                    if (x) return new x();
                    for (i=0; i<y.length; y++) try { 
                    return new ActiveXObject(y[i]);
                    } catch (e) {}
                })(
                    window.XMLHttpRequest, 
                    ['Msxml2.XMLHTTP','Microsoft.XMLHTTP']
                );
            };
            function stream(url) {
                // Declare the variables we'll be using
                var xmlHttp = xmlHttpRequest();
                xmlHttp.open("GET", url, true);
                var len = 0;
                xmlHttp.onreadystatechange = function() {
                if (xmlHttp.status == 200 && xmlHttp.readyState >=3) {
                    var text = xmlHttp.responseText;
                    text = text.substr(len, text.length-len);
                    len = xmlHttp.responseText.length;
                    console.log(text);
                }
                }
                xmlHttp.send(null);
            }           
            stream('/test.php');
        </script>
    </head>
    <body>
    </body>
</html>

Your Mileage May Vary.

Answer №8

According to Jaroslav Moravec's suggestion, changing the content-type in the stream header to application/x-javascript allows it to function properly in Safari, Chrome, and Firefox browsers.

I have yet to verify its compatibility with Internet Explorer.

Answer №9

Andrew's suggestion to set the content type of the return to "application/octet-stream" worked wonders. Additionally, utilizing XDomainRequest on IE is crucial.

If you want to read the incoming data in real-time, consider implementing an infinite loop that stops when either readystate equals 4 or XDomainRequest.onLoad is called.

My approach would be something like this:

let i = 0;
const returnValue = () => {
    if (!finished) {
        setTimeout(returnValue, 100);
    }
    const resp = ajax.responseText;
    const newI = resp.lastIndexOf(";") + 1;
    if (newI > i) {
        const lines = resp.substring(i, newI).split(";");
        for (let x = 0; x < lines.length; x++) {
            eval(lines[x]);
        }
        i = newI;
    }
}

Please note: there are concerns about using eval being risky, but I argue that it's not necessarily where the true risk lies.

Answer №10

Is the URL you're sending the request to within your domain?

This could be due to the same origin policy.

Check out this question and potential ways to bypass it (also see this article).

Answer №11

One time, I encountered an issue while using Safari (I never tested it with Chrome, so there may have been a similar problem there as well since both browsers use the same rendering engine - although I'm not sure about the JavaScript aspects). Unfortunately, I was unable to find a workaround for this issue. Thankfully, since it was just a small app on a company-wide intranet, it wasn't a major concern that Safari wasn't supported (Firefox was the default browser anyway, and it worked fine).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with Django Ajax repeatedly submitting identical data to the endpoint

Having trouble posting the value of an HTML item (the id of the item) to the view in order to add it to the cart. Despite different values being displayed in the HTML source, it always posts the value of the last item generated by the Django {% for %} loop ...

PHP fails to return response to jQuery AJAX request

I am encountering issues with my jQuery AJAX requests in PHP. What could be wrong with my simple example? index.php - includes loading JS, PHP files, and defining a button and a paragraph. <html> <head> <script src='jquery ...

Run a section of code located in a different file

I have defined some global functions in main.js like this: Vue.prototype._isMobile = function () { return $(window).width() < 768 } //Few more similar functions Now, I want to move these functions to a separate file called util.js: return (function ...

Tips for fetching paginated HTTP requests from a backend API using Observables within Angular 4

My Angular 4 app has a service that retrieves a variable number of pages from a back-end API. I came across a solution on Stack Overflow which suggests using flatMap, but it doesn't work for me as the number and URLs of requests are dynamic and unkno ...

What is the best way to determine the size of the URLs for images stored in an array using JavaScript?

I am working on a project where I need to surround an image with a specific sized 'div' based on the image's dimensions. The images that will be used are stored in an array and I need to extract the height and width of each image before disp ...

Passing data between functions in a JavaScript file

I need some guidance on implementing an angularjs google column chart. Specifically, I have a requirement to transfer the value from one JavaScript function to another variable defined in a separate JavaScript function. Here is an example snippet of code ...

Steps to update the color of checkbox labels

I'm struggling with changing the colors of checkbox values based on their status - when checked, I want the text to display in green, and when unchecked, I want it to be red. Below is a sample input type, but I can't figure out how to style the t ...

"The boundary of suspense was updated before completing hydration." This error occurs when utilizing suspense and dynamic import within Next.js

I am currently working on lazy loading a list of data using suspense and dynamic import in Nextjs. However, I encountered the following error: Error: This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch ...

Creating Functional Tabs Using CSS and JavaScript

I've been experimenting with this code snippet, trying to get it to work better. It's still a work in progress as I'm new to this and have only customized it for my phone so far. The issue can be seen by clicking on the Projects and Today ta ...

Instructions for extracting the href value from an anchor tag using JavaScript within a specified string

How can I retrieve the href value of the last anchor tag in the provided content string using pure JavaScript, excluding jQuery? var contents = '<div id="content"><a href="http://www.okhype.com/wp-content/uploads/2016/12/ruffcoin-made-in-aba ...

Adjusting image alignment and formatting long text with CSS in Firefox

I'm attempting to float an image and text within a paragraph, but encountering an issue when the text is long and in one line. The display appears fine in Chrome, but not in Mozilla Firefox. You can view the problem here: In Chrome, it looks like thi ...

The reset function is malfunctioning on the meteor entity

Currently, I am going through an angular-meteor tutorial that can be found here I seem to be facing some issues with my code. Here's what I have: angular.module('socially').controller('PartyDetailsCtrl', function($scope, $statePa ...

Establishing the Header for CORS AJAX Request

I am attempting to initiate an AJAX call from one server to a JSON endpoint on another server. I have come across suggestions regarding setting the CORS header, but I am unsure about the specific steps required. The issue arises when making a request from ...

The production deployment of Heroku using Nuxt is configured with staging environment variables

My deployment process involves using Heroku to deploy my Nuxt.js frontend app, where I have configured a variable for the API URL. However, whenever I move an app from staging to production, the production site ends up using the configuration variables fr ...

What strategies can I use to make my div content more adaptable to different screen sizes and

Currently, in my project, I have implemented a layout using the top nav menu (purple part) and the left menu (pink part) as shown in the image. However, I am facing an issue where, upon deleting some content from the view, the pink menu on the left extends ...

Encountering an issue when attempting to bring in a new library

When attempting to import a library, I encountered this error. https://i.sstatic.net/NYYQX.png The command used to obtain this library was: npm i queue Here is how I attempted to import it in my javascript: import Queue from "./node_modules/queue/ ...

targets the buildDependencies folder, not the specific file

When using caching (type: 'filesystem'), I encountered an interesting issue. buildDependencies: { config: [__filename], } After making a minimal change and rebuilding, the cache was successfully utilized: [webpack.cache.PackFileCacheStrategy ...

How come the callback in Jquery fadeOut keeps looping repeatedly, and what can I do to stop this from happening?

My approach involves fading out a div box and implementing a callback function as shown below: function closeWindow(windowIdPrefix, speed) { $("#" + windowIdPrefix + "_ViewPanel").fadeOut(speed, function() { resetWindow(windowIdPre ...

Guide to obtaining specific top elements from an array using JavaScript

I'm seeking assistance with sorting arrays in JavaScript. Here is an example of a sorted array: mainArray : [25 20 20 20 18 17 17 15 12 12 10 5 5 ] The mainArray may contain duplicate values. A. Dealing with duplicates Based on user input, I need ...

Guide on scheduling MongoDB calls with Node.js recursively

Is there a way to trigger this code or function periodically? I am considering using setInterval, for example: setInterval('function()', 5000);. However, I am unsure about how to implement it in this specific scenario. list = []; MongoClient.conn ...