Home > database >  Node.js Express not waiting for Async Await
Node.js Express not waiting for Async Await

Time:07-25

I have a program that I'm trying to run, and when getting the cars, it needs to wait for the function to finish before running the result, but it doesn't seem to be working. I'm new to this, so I don't really know what I'm doing all that well.

app.get('/cars', async (req, res) => {
  try {
    const result = await getCars()
    console.log('result: '   result)
  res.send(result)
  } catch(error) {
    console.log(error)
  }
  
})

That's the code to call the "getCars" function which is below:

const getCars = async () => {
  // TODO: Replace this with a call to the database
  await fs.readFile(__dirname   '/cars.json', function (err, data) {
    if (err) {
      throw err
    }
    let cars = JSON.parse(data)

    //Groups the cars to their location, to be sorted and chosen based on arrivalDate
    const groupBy = (xs, f) => {
      return xs.reduce(
        (r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r),
        {}
      )
    }

    const result = groupBy(cars, (c) => c.locationName)

    Object.keys(result).forEach((car) => {
      console.log(car)
      // filters out cars with no arrival dates
      let filtered = result[car].filter(obj => Object.keys(obj).includes("arrivalDate"));

      //Sort cars by the last update time
      filtered.sort(function (a, b) {
        let keyA = new Date(a.arrivalDate.toString().split(' ')[0]),
          keyB = new Date(b.arrivalDate.toString().split(' ')[0])
        // Compare the 2 dates
        if (keyA < keyB) return -1
        if (keyA > keyB) return 1
        return 0
      }).reverse()
      

      //Add the top two (latest) of each car to the new array  
      emailCars = [...emailCars, { [car]: [ result[car][0], result[car][1] ]}]

    })
    console.log('returning'   emailCars)
    return emailCars
  })
}

What am I missing here to make sure emailCars is being set by the function and then sent to the user when they go to /cars

CodePudding user response:

I believe that problem is with the very first line of the getCars() function...

await fs.readFile(__dirname   '/cars.json', function (err, data) {
}

You cannot await a function that returns results in a callback. Either use "sync" version of readFile and remove await/async from getCars(), or use promisfied version of readFile:

try {
const data = await fs.promises.readFile(filepath, 'utf8');
} catch (e) {
 // handle errors herer
}

CodePudding user response:

You should first understand sync/asynchronous concept. Here is my solution.

import express from 'express'
import { readFile } from 'fs'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { promisify } from 'util'

const app = express()

const readFileAsync = promisify(readFile)

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const getCars = async () => {
  const file = await readFileAsync(`${__dirname}/cars.json`, 'utf8')
  const cars = JSON.parse(file)

  // DO YOUR LOGIC HERE

  return cars
}

app.get('/', async (req, res) => {
  try {
    const cars = await getCars()
    res.json(cars)
  } catch (error) {
    console.log(error)
  }
})

app.listen(7777, () => {
  console.log('Example app listening on port 7777!')
})

CodePudding user response:

The only issue that I see is you are mixing async/await with callback notion together. Just replace all the callbacks with async/await and wrap them around try/catch.

const getCars = async () => {
    // TODO: Replace this with a call to the database
    try {
        const data = await fs.readFile(__dirname   '/cars.json') 
            let cars = JSON.parse(data)
        
            //Groups the cars to their location, to be sorted and chosen based on arrivalDate
            const groupBy = (xs, f) => {
              return xs.reduce(
                (r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r),
                {}
              )
            }
        
            const result = groupBy(cars, (c) => c.locationName)
        
            Object.keys(result).forEach((car) => {
              console.log(car)
              // filters out cars with no arrival dates
              let filtered = result[car].filter(obj => Object.keys(obj).includes("arrivalDate"));
        
              //Sort cars by the last update time
              filtered.sort(function (a, b) {
                let keyA = new Date(a.arrivalDate.toString().split(' ')[0]),
                  keyB = new Date(b.arrivalDate.toString().split(' ')[0])
                // Compare the 2 dates
                if (keyA < keyB) return -1
                if (keyA > keyB) return 1
                return 0
              }).reverse()
              
        
              //Add the top two (latest) of each car to the new array  
              emailCars = [...emailCars, { [car]: [ result[car][0], result[car][1] ]}]
        
            })
            console.log('returning'   emailCars)
            return emailCars
    } catch (e) {
        console.log(e);
    }
    
  }

If you have any difficulty understanding this concept, please check this link.

if I do that, it returns all the sorted data, but I only need the first 2 of each sub-array in the object (thats what the emailCars does, gets just the first 2 objects in each array). Or should I do that in the /get function instead?

Any utility function should be correctly organised in order to main the correct structure of your application. The routes should not contain so much logic and the logic should be placed under controller or anything like that.

You need to devise your own logic and if you face any issues, you are welcome to ask on this platform.

  • Related