Jeremiah
Jeremiah

Reputation: 1

async/await fetch API call that maintains the order

I want to make an async fetch call with await operator. A forEach method iterates over an array of API endpoints, then inserts some values into a local object. See below.

It works great, except - sometimes the values entered into dataModel[index].retailers[i+1] does not align with the index. For example, products for api object 2 will be inserted into local object 4.

I think this has something to do with the asynchronous nature of the request, I thought await might resolve it but it hasn't. Can anyone show me the way?

var api_obj;
// Defining async function
async function getapi(url, index) { // Storing response
  var response = await fetch(url)
    .then(res => res.json())
    .then(data => api_obj = data)
    .then(function() {

      //iterate over the API object and store required values 
      //in our local dataModel object
      Object.keys(api_obj[0].RetailerProducts).forEach(function(i) {
        var i = parseInt(i, 10)
        dataModel[index].retailers[i + 1] = {
          retailer: api_obj[0].RetailerProducts[i].RetailerName,
          url: api_obj[0].RetailerProducts[i].ClickThruUrl,
          price: api_obj[0].RetailerProducts[i].Price.toFixed(2),
          logo: api_obj[0].RetailerProducts[i].RetailerLogoUrl,
          name: api_obj[0].RetailerProducts[i].RetailerProductName
        }
      })

    });

}

//iterate over dataModel and call each API endpoint
Object.keys(dataModel).forEach(function(i) {
  // Calling that async function
  getapi(dataModel[i].apiEndpoint, i);
})

Here is what dataModel looks like:

var dataModel = {
  '1': {
    'retailers': {}
  },
  '2': {
    'retailers': {}
  },
  '3': {
    'retailers': {}
  }
}

Upvotes: 0

Views: 745

Answers (1)

Jeremiah
Jeremiah

Reputation: 1

I figured out how to solve the ordering issue, here was the solution, basically utilising another .then method that calls the next function and a counter sitting in global scope:

var apiCounter = 1;

let csApiCall = () => {
  var api_obj;
  // Defining async function
  async function getapi(url, index) { // Storing response
    var response = await fetch(url)
      .then(res => res.json())
      .then(data => api_obj = data)
      .then(function() {

        //iterate over the object and store required values 
        //in our dataModel object
        Object.keys(api_obj[0].RetailerProducts).forEach(function(k) {
          var k = parseInt(k, 10)
          dataModel[index].retailers[k + 1] = {
            retailer: api_obj[0].RetailerProducts[k].RetailerName,
            url: api_obj[0].RetailerProducts[k].ClickThruUrl,
            price: api_obj[0].RetailerProducts[k].Price.toFixed(2),
            logo: api_obj[0].RetailerProducts[k].RetailerLogoUrl,
            name: api_obj[0].RetailerProducts[k].RetailerProductName
          }
        })

      })
      .then(function() {

        if (apiCounter < Object.keys(dataModel).length) {
          apiCounter++;
          getapi(dataModel[apiCounter].apiEndpoint, apiCounter);
        }

      });

  }

  getapi(dataModel[apiCounter].apiEndpoint, apiCounter);

};

Upvotes: 0

Related Questions