Home > Back-end >  How to return a Promise with consequential axios calls?
How to return a Promise with consequential axios calls?

Time:06-06

I need to create a function, that will return a Promise and will call another function that will have an axios.get() call. Axios.get() calls an API that returns a data with the following structure:

{
  count: 87, //total number of records
  next: '[api_url]/&page=2'//indication if the API has a next page
  previous: null, ////indication if the API has a previous page, if call URL above - it will return [api_url]/&page=1
  results: [...] //10 objects for each call, or remaining for the last page
}

Since I know that only 10 results are being returned on every call, I need to check if the returned object has the next key, and if it does - make another call and so on, until no more next. I need to concatenate all the results and eventually resolve the Promise returned from the main function with all the data.

So I tried something like that:

const fetchResource = async({type, search, page}) {
  const data = {count: 0, results: []}
  const request = await performFetch({type, search, page}, data).then((data) => {
    console.log('data?', data)
  })
  console.log('req', request)
}

const performFetch = async({type, search, page}, result) => {
  const params = {
    page
  }
  if (search) {
    params.search = search
  }

  await axios.get(`${type}/`, {
    params
  }).then(async({data}) => {
    result.results = [...result.results, ...data.results]
    result.count = data.count
    if (data.next) {
      page  = 1
      await performFetch({type, search, page}, result)
    } else {
      console.log('result', result)
      return result
    }
  })
    .catch((err) => {
      console.error(err)
    })
}

Now I see that once I call fetchResourche all the requests are going out, and in console.log('result', result) I do see the concatenated data:

{
  count: 87,
  results: [/*all 87 objects*/]
}

But console.log('data?', data) and console.log('req', request) both print out undefined.

Where I return result, I tried to return Promise.resolve(result) - same result.

And I'm not sure how to return a Promise here, that will resolve once all the API calls are concluded and all the data is received. What am I missing? How do I make it work?

CodePudding user response:

Couple of observations regarding your code:

  • There's no need to mix async-await syntax with promise chaining, i.e. then() and catch() method calls

  • Inside performFetch function, you need an explicit return statement. Currently, the function is implicitly returning a promise that fulfils with the value of undefined.

    Key point here is that the performFetch function is returning before you get the result of http requests to the API. It isn't waiting for the result of HTTP requests to be returned before returning.

Following is a simple demo that illustrates how you can make multiple requests and aggregate the data until API has returned all the data.

let counter = 0;

function fakeAPI() {
  return new Promise(resolve => {
    setTimeout(() => {
      if (counter < 5) resolve({ counter: counter   });
      else             resolve({ done: true });
    }, 1000);
  });
}

async function performFetch() {
  const results = [];
  // keep calling the `fakeAPI` function
  // until it returns "{ done = true }"
  while (true) {
    const result = await fakeAPI();
    if (result.done) break;
    else results.push(result);
  }
  return results;
}

performFetch().then(console.log).catch(console.log);
<small>Wait for 5 seconds</small>

Your code can be rewritten as shown below:

const fetchResource = async ({ type, search, page }) => {
  const data = { count: 0, results: [] };
  const result = await performFetch({ type, search, page }, data);
  console.log(result);
};

const performFetch = async ({ type, search, page }, result) => {
  const params = { page };
  if (search) params.search = search;

  while (true) {
    const { data } = await axios.get(`${type}/`, { params });
    result.results = [...result.results, ...data.results];
    result.count = data.count;

    if (data.next) page  = 1;
    else           return result;
  }
};

Ideally, the code that calls the fetchResource function should do the error handling in case any of the HTTP request fails.

  • Related