Search code examples
javascriptxmlhttprequest

Do an array of on readystage function


I have an array of XMLHttpRequest (working fine) and I want to know when they are all finished. I wrote the following array of on readystatechange functions:

xhr[i].open('post', 'PHP/write_and_exec_opt.php');
display_opt[i] = xhr[i].onreadystatechange = function(index) {
    if (xhr[index].readyState == 4 && xhr[index].status == 200) {
        text = xhr[index].responseText.trim();
        n_finished++;
        console.log('display_opt', n_finished);
    }
    xhr[index].send(data);
}(i);

The xhr requests are executed properly. But no output on the console log. Why not?


Solution

  • You're really close, but by executing your anonymous function, you end up assigning its return value to the onreadystatechange property. You never return a value, so you assign undefined. You probably meant to return a function:

    xhr[i].open('post', 'PHP/write_and_exec_opt.php');
    display_opt[i] = xhr[i].onreadystatechange = function(index) {
        return function() {
            if (xhr[index].readyState == 4 && xhr[index].status == 200) {
                text = xhr[index].responseText.trim();
                n_finished++;
                console.log('display_opt', n_finished);
            }
        };
    }(i);
    xhr[i].send(data);
    

    That said, it gets confusing fast and there's no good reason to create the temporary function repeatedly, which is why I usually break things out:

    Somewhere before (or after, but in any case in the same fundamental scope) your i loop:

    function createStateChangeHandler(index) {
        return function() {
            if (xhr[index].readyState == 4 && xhr[index].status == 200) {
                text = xhr[index].responseText.trim();
                n_finished++;
                console.log('display_opt', n_finished);
            }
        };
    }
    

    Then in your i loop:

    xhr[i].open('post', 'PHP/write_and_exec_opt.php');
    display_opt[i] = xhr[i].onreadystatechange = createStateChangeHandler(i);
    xhr[i].send(data);
    

    For what it's worth, I'd suggest looking into using promises for this sort of thing, esp. now that they're a standard part of JavaScript (as of ES2015):

    Promise.all(xhrArray.map(function(xhr) {
        return new Promise(function(resolve, reject) {
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4) {
                    if (xhr.status == 200) {
                        resolve(xhr.responseText.trim());
                    } else {
                        reject(/*...include some error perhaps...*/);
                    }
                }
            };
            xhr[index].send(data);
        });
    })
    .then(function(array) {
        // `array` contains an array o the results, in the same
        // order as `xhrArray`
    })
    .catch(function() {
        // At least one XHR failed
    });