DEV Community

tsanak
tsanak

Posted on

Datatables: Combine multiple ajax sources

The problem

Recently I had to fetch data from 2 sources and display them in a datatable.
The only requirement was to not show the data in the datatable until all the requests where finished. The current function, that worked for 1 source, was something like the one below (not the exact function 😅)

/* 
dt is a variable that holds the DataTable object
*/
function fetchData(dt, url) {
    $.ajax({
        url,
        dataType: 'json'
    })
    .done(function(res) {
        res.forEach(a => {
            dt.row.add([
                a.id,
                a.name,
                a.username,
                a.email,
                a.phone
            ]).draw(false);
        });
    })
    .fail(function(err) {
        reject('error');
    })
}
Enter fullscreen mode Exit fullscreen mode

Why not call fetchData twice, one for each data source?

That would fill the datatable with data from all the sources, BUT it would also violate my only requirement (do not show the data until all the requests are finished).

Since I had to wait I thought about two solutions:

  1. Promises
  2. Async / Await

Promises

One promise is created for each data source.

They are sent together and when all of them return (Promise.all) then the data is added to the datatable.

function fetchData(url) {
    return new Promise((resolve, reject) => {
        $.ajax({
            url,
            dataType: 'json'
        })
        .done(function(res) {
            resolve(res);
        })
        .fail(function(err) {
            reject('error');
        })
    });
}

$(document).ready(function() {
    var dt = $('#example').DataTable({
        "ajax": function (d, callback, s) {
            let promisesArr = [];
            promisesArr.push(fetchData('/one-server-api/1'));
            promisesArr.push(fetchData('/another-server-api/2'));
            promisesArr.push(fetchData('users.json'));
            Promise.all(promisesArr).then(values => {
                // Convert [[1, 2], [3, 4], [5, 6]] to [1, 2, 3, 4, 5, 6]
                let all = values.reduce((accumulator, currentValue) => [...accumulator,...currentValue]);
                all.forEach(a => {
                    dt.row.add([
                        a.id,
                        a.name,
                        a.username,
                        a.email,
                        a.phone
                    ]).draw(false);
                });

            });
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

Async / Await

The solution is quite similar.

function fetchData(url) {
    return $.ajax({
            url,
            dataType: 'json'
        })
        .done(function(res) {
            return res;
        })
        .fail(function(err) {
            return 'error';
        });
}

$(document).ready(function() {
    var dt = $('#example').DataTable({
        "ajax": async function (d, callback, s) {
            let all = [];
            all.push(...await fetchData('/one-server-api/1'));
            all.push(...await fetchData('/another-server-api/2'));
            all.push(...await fetchData('users.json'));
            all.forEach(a => {
                dt.row.add([
                    a.id,
                    a.name,
                    a.username,
                    a.email,
                    a.phone
                ]).draw(false);
            });
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

Both promises and async/await solves the issue, but they do it, in a different way.

The key difference between the solutions is the time that they send the requests

The Promise solution sends all the requests at the (nearly) same time, the async solution waits for each request to finish before continuing to the next one.

So if you had 2 endpoints (sources):

  1. Endpoint 1 (E1): with an average response time of 1000ms
  2. Endpoint 2 (E2): with an average response time of 800ms

Promises would:

  • send the request to E1,
  • send the request to E2,
  • wait until both requests finish (avg 1000ms)
  • add data

Total average time before adding data: 1000ms

Promises solution request timings

Promises solution request timings

Async/await would:

  • send the request to E1,
  • wait until E1 request finishes (avg 1000ms)
  • send the request to E2,
  • wait until E2 request finishes (avg 800ms)
  • add data

Total average time before adding data: 1800ms

Async solution request timings

Async solution request timings

Top comments (2)

Collapse
 
van9petryk profile image
van9petryk • Edited

and how to hide 'Loading' label then?

Collapse
 
van9petryk profile image
van9petryk • Edited

Ok, I solved this.

Replaced

    all.forEach(a => {
        dt.row.add([
            a.id,
            a.name,
            a.username,
            a.email,
            a.phone
        ]).draw(false);
    });
Enter fullscreen mode Exit fullscreen mode

with this one:

    callback({
      'data': all
    });
Enter fullscreen mode Exit fullscreen mode