How to make callback run asynchronously?

I have a JavaScript function called getAandB which takes a callback. getAandB firstly gets value ‘a’ using ajax. It then invokes the callback with value ‘a’ as an argument. The callback gets value ‘b’ and console.logs both ‘a’ and ‘b’ to the console. so I get {"key":"a"} {"key":"b"} in the console.

I thought that the two ajax calls would happen simultaneously / asynchronously. However, they seem to run one after the other ie. synchronously.

The JavaScript code and the PHP code for the ajax requests is shown below:

index.html:

<script>
    function getAandB(callback){
        const xhr = new XMLHttpRequest();
        xhr.open('GET', './ajax-a.php', true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 && xhr.status === 200){
                callback(xhr.responseText)
            }
        }
        xhr.send();
    }

    function callback(resultA){
        const xhr = new XMLHttpRequest();
        xhr.open('GET', './ajax-b.php', true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 && xhr.status === 200){
                const resultB = xhr.responseText;
                console.log(resultA, resultB);
            }
        }
        xhr.send();
    }
    getAandB(callback);
</script>

ajax-a.php:

<?php
sleep(5);
$response = [
    "key" => "a",
];
echo json_encode($response);

The code for ajax-b.php is the same as for ajax-a.php except the value of $response.key is b not a.

I thought that the above code would result in ajax calls being made simultaneously to get ‘a’ and ‘b’. However if the PHP code sleeps for 5 seconds for both ajax-a.php and ajax-b.php, then it takes 10 seconds for the console.log to appear. If only one of the ajax-?.php scripts sleeps for 5 seconds then it takes 5 seconds for the console.log to appear.

How can I use callbacks to allow me to combine the results of ajax calls, as I have done here, but to make the individual calls happen simultaneously / asynchronously? Alternatively, is not possible to implement this with callbacks?

>Solution :

If you want the request to ajax-b to me made at approximately the same time as the request for ajax-a then you need to make the respective calls to xhr.send() at approximately the same time.

At the moment, the call to ajax-b‘s send() takes place as part of callback() which you only call after you have received the response to the request for ajax-a.


You then need to add additional logic to determine when you have received both responses so you log both bits of data at the same time (assuming you still want to do that).

A rough and ready way to do that, keeping to your current approach, would look something like this:

function getA(callback){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', './ajax-a.php', true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
            callback(xhr.responseText)
        }
    }
    xhr.send();
}

function getB(callback){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', './ajax-b.php', true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
            const resultB = xhr.responseText;
            callback(xhr.responseText)
        }
    }
    xhr.send();
}

function getAandB() {
    const data = [];

    function callback(responseData) {
        data.push(responseData);
        if (data.length === 2) {
            console.log(...data);
        }
    }

    getA(callback);
    getB(callback);
}

getAandB();

We have better tools for that these days though, thanks to promises and modern APIs (like fetch) which support them natively.

async function getAandB() {
    const dataPromises = [
        fetch("./ajax-a.php").then(r => r.text()),
        fetch("./ajax-b.php").then(r => r.text())
    ];
    const data = await Promise.all(dataPromises);
    console.log(...data);
}
getAandB();

Leave a Reply