2

I am working on a nodejs code that fetches data from a site, parses it, finds particular data and fetches something else for the data that was previously fetched. But the final return statement is returning without the value fetched from the second API call.

I tried to implement async await, but I am not sure where do I have to put them exactly.

const getMainData = async val => {
  let result = [];

  //get xml data from the API
  const xmlData = await getSiteContent(`value`); //axios call

  parseString(xmlData, (err, json) => { //convert xml to json

    const { entry } = json.feed; // array of results.
    result = entry.map(report => {
      const secondInfo = getSomeMoreData(report.something); //axios call
      const data = {
        id: report.id,
        date: report.date,
        title: report.title
      };
      data.info = secondInfo;
      return data;
    });

  });


  return { result };
};

I was expecting the function to return the array result that has id, date, title and info. But I am getting info as null since it is going to another function that does one more API call.

Vishwasa Navada K
  • 572
  • 1
  • 8
  • 30
  • 1
    Possible duplicate of [How do I convert an existing callback API to promises?](https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises) – CertainPerformance May 14 '19 at 03:44
  • 1
    also https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call and https://stackoverflow.com/questions/31426740/how-to-return-many-promises-in-a-loop-and-wait-for-them-all-to-do-other-stuff (use `Promise.all`) – CertainPerformance May 14 '19 at 03:45

2 Answers2

1

Try wrapping parseString in a promise so you can await the result, then make the entry.map callback an async function so that you can use the await keyword to wait for the result of the axios fetch.

async function xml2json(xml) {
  return new Promise((resolve, reject) => {
    parseString(xml, function (err, json) {
      if (err)
        reject(err);
      else
        resolve(json);
    });
  });
}

const getMainData = async val => {
  //get xml data from the API
  const xmlData = await getSiteContent(`value`); //axios call

  const json = await xml2json(xmlData);
  const { entry } = json.feed; // array of results

  const result = await Promise.all(
    entry.map(async report => {
      const secondInfo = await getSomeMoreData(report.something); // axios call
      const data = {
        id: report.id,
        date: report.date,
        title: report.title,
      };
      data.info = secondInfo;
      return data;
    })
  )

  return { result };
}

Let me know if that works. If not, I can try to help you out further.

mepley
  • 470
  • 4
  • 18
  • 1
    The callback() for entry.map() is made to be an async function which means result would be an array full of promises right? So shouldn't it need to be resolved, eg using [Promises.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) which would resolve when all of the promises in array is resolved. – PrivateOmega May 14 '19 at 04:42
  • @mepley your code works. thank you! Now I understood what was going wrong. – Vishwasa Navada K May 14 '19 at 16:57
  • Thanks @KiranMathewMohan for noticing that I forgot Promise.all() around the array of promises! – mepley May 14 '19 at 18:15
1

The problem with your code is you have mixed promises concept(async/await is a syntactic sugar - so same thing) along with callback concept.

And the return statement is outside callback() of parseString() and the callback would be executed maybe after returning results only because parseString() is an asynchronous function.

So in the following solution I have wrapped parseString() in a promise so that it can be awaited.

const parseStringPromisified = async xmlData => {
  return new Promise((resolve, reject) => {
    parseString(xmlData, (err, json) => {
      if (err) {
        reject(err);
      }
      resolve(json);
    });
  });
};

const getMainData = async val => {
  //get xml data from the API
  const xmlData = await getSiteContent(`value`); //axios call
  const json = await parseStringPromisified(xmlData);
  const { entry } = json.feed; // array of results.
  const result = entry.map(async report => {
    const secondInfo = await getSomeMoreData(report.something); //axios call
    return {
      id: report.id,
      date: report.date,
      title: report.title,
      info: secondInfo
    };
  });
  return Promises.all(result);
};
PrivateOmega
  • 2,509
  • 1
  • 17
  • 27