0

I'm trying to add all mongoDB results to an array and send it as a post response. The individual console.logs for each response from mongodb outputs data. But when i try to push that data into the events array it doesn't seem to work. When i do console.log(events) at the end, it just logs an empty array. Any ideas?

calendar.post('/getEvents', (req, res) => {
  let events = []
  // Add all budget dates to events array
  Budget.find({
    userEmail: req.body.userEmail
  })
    .then(budgets => {
      // console.log(budgets)
      for (let i = 0; i < budgets.length; i++) {
        events.push(budgets[i])
      }
    })
  // Add all notes dates to events array
  Notes.find({
    userEmail: req.body.userEmail
  })
    .then(notes => {
      // console.log(notes)
      for (let i = 0; i < notes.length; i++) {
        events.push(notes[i])
      }
    })
  // Add all study plan dates to events array
  StudyPlan.find({
    userEmail: req.body.userEmail
  })
    .then(studyplans => {
      // console.log(studyplans)
      for (let i = 0; i < studyplans.length; i++) {
        events.push(studyplans[i])
      }
    })
  // Add all todo list dates to events array
  TodoList.find({
    userEmail: req.body.userEmail
  })
    .then(todolists => {
      // console.log(todolists)
      for (let i = 0; i < todolists.length; i++) {
        events.push(todolists[i])
      }
    })
  console.log(events)
  res.send(events)
})

EDIT:

This is the console after i make a post request to this route (I added 'Budgets:' and 'Events:' to the console logs to make it easier to read):

Events: []
Budgets: [ { duration: 7,
    _id: 5ccd88cb4c13380d84446673,
    title: 'bla',
    amount: null,
    startDate: 2019-05-04T12:42:45.000Z,
    userEmail: 'test@gmail.com',
    spending: [],
    __v: 0,
    expenses: [] },
  { duration: 7,
    _id: 5ccd89c04c13380d84446674,
    title: 'bla2',
    amount: null,
    startDate: 2019-05-04T12:46:52.000Z,
    userEmail: 'test@gmail.com',
    spending: [],
    __v: 0,
    expenses: [] } ]
dillon
  • 721
  • 6
  • 16
Adam Cole
  • 173
  • 1
  • 3
  • 13

2 Answers2

0

Javascript's asynchronous is showing. You're console logging and sending the response before any of your queries actually complete.

Here is a solution that I've drafted up that does what you're trying to accomplish

calendar.post('/getEvents', async (req, res) => {
    const { userEmail } = res.body;

    try {
        const results = await Promise.all([
            Budget.find({userEmail}),
            Notes.find({userEmail}),
            StudyPlan.find({userEmail}),
            TodoList.find({userEmail}),
        ]);

        const events = [].concat.apply([], results);

        console.log(events);
        res.send(events);
    }
    catch (error) {
        return res.status(500).send(error);
    }
});

Essentially await Promise.all waits (as implied), for the provided array of promises to all resolve. These queries are all run in parallel and are returned into the results variable.

results at this point is a nested array like so:

results = [
[...], // Budget query results
[...], // Notes query results
[...], // StudyPlan query results
[...], // TodoList query results
]

[].concat.apply([], results) is shorthand for "flattening" an array of arrays.

This solution takes advantage of async/await. async/await allows you to write javascript in a manner that looks synchronous. You can read up about it here MDN async function

dillon
  • 721
  • 6
  • 16
  • I'm not entirely sure how this works. Also, this code is just a test to see how i would push data from the database to the user in my specific scenario. For each `.find` i want to manipulate the data before i put it into the events array. Is there no simpler way to ensure that events is sent last? – Adam Cole May 09 '19 at 01:18
  • Ah ok so would i just `.then` etc inside the `await Promise.all` block? – Adam Cole May 09 '19 at 01:20
  • You _could_ use `.then` inside the `await Promise.all` block, but it could get messy depending on how you're planning to manipulate the individual query results. I'm curious as to what you're trying to do with the results? I'm curious if mongoose is already able to perform the data manipulation that you're going for? – dillon May 09 '19 at 01:27
  • Im using a VueCal calendar and it has a specific format for its events. So i need to take all date and time data from my database and format in a way that VueCal accepts. – Adam Cole May 09 '19 at 01:28
  • Hmm.. how are you storing the date values on these events? VueCal _should_ accept a standard ISO date format. Are you not storing the dates this way? You _should_ be able to pass them directly through without manipulation, eh? – dillon May 09 '19 at 01:32
  • I'm storing them in different collections which have different schema layouts. Also VueCal doesn't seem to accept ISO date format, just 'YYYY-MM-DD HH:mm'. I just see it as being far too messy if i do all of this on the front end, i would rather have the server create an events object that i can push to the user and then just inject into the calendar. – Adam Cole May 09 '19 at 01:39
  • @CrankyCoder javascript is synchronous in nature and runs on a single thread. What you described here is sync nature and not async nature. That's why we use Promises which helps us to deal with async operations in javascript. I think you got confused there mate. Take a look at this stackoverflow post, it will clear some things up https://stackoverflow.com/questions/2035645/when-is-javascript-synchronous – cEeNiKc May 09 '19 at 03:06
  • I understand that Javascript functions synchronously for certain operations, but we're not referring to the synchronous operations of Javascript here. The OP's question is regarding async operations of querying a database. async/await allows asynchronous operations to appear lexically synchronous. – dillon May 09 '19 at 03:18
0

Thanks to Cranky Coder who informed me on the asynchronous nature of JavaScript. This prompted me to do some extra research and changing my code to the following allowed me to send a response with the events in through a synchronous manner:

const events = []

const getDates = async (userEmail) => {
  Budget.find({
    userEmail: userEmail
  })
    .then(budgets => {
      // console.log('Budgets:', budgets)
      for (let i = 0; i < budgets.length; i++) {
        console.log(budgets[i])
        events.push(budgets[i])
      }
    })
  // Add all notes dates to events array
  Notes.find({
    userEmail: userEmail
  })
    .then(notes => {
      // console.log(notes)
      for (let i = 0; i < notes.length; i++) {
        console.log(notes[i])
        events.push(notes[i])
      }
    })
  // Add all study plan dates to events array
  StudyPlan.find({
    userEmail: userEmail
  })
    .then(studyplans => {
      // console.log(studyplans)
      for (let i = 0; i < studyplans.length; i++) {
        console.log(studyplans[i])
        events.push(studyplans[i])
      }
    })
  // Add all todo list dates to events array
  TodoList.find({
    userEmail: userEmail
  })
    .then(todolists => {
      // console.log(todolists)
      for (let i = 0; i < todolists.length; i++) {
        console.log(todolists[i])
        events.push(todolists[i])
      }
    })

  return events
}

calendar.post('/getEvents', async (req, res) => {
  // Add all budget dates to events array
  getDates(req.body.userEmail).then(events => {
    res.send(events)
  })
})
Adam Cole
  • 173
  • 1
  • 3
  • 13