Datatables: Combine multiple ajax sources
Tags: javascript, promises, async, datatable
Published: 2020-02-04 22:55:22

---
title: Datatables: Combine multiple ajax sources
published: true
description: Combining multiple ajax sources with the help of promises and async/await
tags: javascript, promises, async, datatable
---

## 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* 😅)

```javascript
/* 
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');
    })
}
```

### 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](https://javascript.info/promise-basics)
2.  [Async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) / [Await](https://javascript.info/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.

```javascript
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);
                });

            });
        }
    });
});
```

## Async / Await

The solution is quite similar.
```javascript
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);
            });
        }
    });
});
```

## 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](https://dev-to-uploads.s3.amazonaws.com/i/d1tp9zgwl1frfcmpya6t.gif)
<figcaption>Promises solution request timings</figcaption>

`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](https://dev-to-uploads.s3.amazonaws.com/i/wuvl0bnvv4jdaiv91hu8.gif)
<figcaption>Async solution request timings</figcaption>