0

I am trying to use the async await.

My app starts like this

axios.get(`${ROOT_URL}/ccidxann.php`)
  .catch(err => console.error('axios error get', err))
  .then(async res => {
    const html = res.data
    const jobList = await getJobList(html)
    console.log(' after each jobList', jobList)
    // jsonfile.writeFileSync('jobs.json', jobList, { flag: 'a' })
  })
  .catch(err => console.error(err))

The problem is that jobList is always returned as an empty array. Here is the getJobList function

async function getJobList (html) {
  const jobList = []

  const dom = new JSDOM(html)
  const { window } = dom
  const $ = require('jquery')(window)
  const jobsInDom = $('table').first().find('td > a')

  // AWAIT HERE
  await jobsInDom.each(async function (index, jobElem) {
    const name = $(jobElem).text()
    const url = $(jobElem).attr('href')
    // Another AWAIT HERE
    const jobDetailsHTML = await getJobDetailsHTML(`${ROOT_URL}/${url}`)
      .catch(err => console.error('getJobDetailsHTML err', err))
    const domDetails = new JSDOM(jobDetailsHTML)
    const { window: windowDetails } = domDetails
    const $details = require('jquery')(windowDetails)
    const jobDetailsHTMLBody = $details('body').prop('outerHTML')
    jobList.push({
      _id: url,
      name,
      detailsHTML: jobDetailsHTMLBody
    })
    console.log('in each jobList', jobList)
  }) //each
  return jobList
}

As you can see I put the async keyword in front of it,

and I have put the async keyword in the callback of each

and put await in front of all async operations.

The console.log in the callback of each prints the array filled with values

but it seems the return works before the each

even with await keyword in front of it.

And also here is getJobDetailsHTML just in case. It's a simple function and seems to work just fine

async function getJobDetailsHTML (url) {
    // ANOTHER AWAIT HERE
    return await axios.get(url).data
}
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Hayk Safaryan
  • 1,996
  • 3
  • 29
  • 51

1 Answers1

1

I think jobsInDom.each is synchronous function, so putting await before doesn't give you desired effect. So somehow you need to get promise which resolved when processing is finished for all jobs, like this:

  // handles one element and returns promise
  const jobHandler = async jobElem => {
    const name = $(jobElem).text()
    const url = $(jobElem).attr('href')
    const jobDetailsHTML = await getJobDetailsHTML(`${ROOT_URL}/${url}`)
      .catch(err => console.error('getJobDetailsHTML err', err))
    const domDetails = new JSDOM(jobDetailsHTML)
    const { window: windowDetails } = domDetails
    const $details = require('jquery')(windowDetails)
    const jobDetailsHTMLBody = $details('body').prop('outerHTML')
    jobList.push({
      _id: url,
      name,
      detailsHTML: jobDetailsHTMLBody
    })
    console.log('in each jobList', jobList)
  }

  const promises = [];

  // start processing of each element
  jobsInDom.each((index, jobElem) => promises.push(jobHandler(jobElem));

  // wait for processing of all job elements
  await Promise.all(promises);
udalmik
  • 7,838
  • 26
  • 40