0

I know this question has been asked a few times, but none seem to answer the particular part of using the query results for later.

I also know the problem resides on the fact that queries are asynchronous, and perhaps this is the reason I cannot seem to find a satisfactory answer.

Here's what I'm trying to do:

I have a node project with several sections, each section with different content. These sections have individual properties, which I decided to store in a Model for later use.

So far (and for simplicity sake) I have the following schema:

const SectionSchema = new Schema({
    name: String,
    description: String
})

const Section = mongoose.model('Sections',SectionSchema)

I'd like to retrieve this data to be used in one of my layouts (a navigation header), so I tried something like this:

const express = require('express')
const app = express()

Section.find().then(function(docs){
    app.locals.sections = docs
})

console.log(app.locals.sections) // undefined

This obviously doesn't quite work due to find() being asynchronous, or rather, it does work but the values are populated at a different time. I know that if I do the console.log check inside the function I'd get a result, but that's not the concern, I want to store the data in app.locals so that I could later use it in one of my layouts.

Ideally I'd like to load this data once, before the server begins to listen to requests.

Feel free to correct me if I've made any wrong assumptions, I'm very new to node, so I don't quite know how to approach things quite yet.

Thanks in advance.

EDIT: I should've mentioned I'm using express.

dev404
  • 1,088
  • 13
  • 34
  • That's the same thing I'm doing, just using a different variable. Either way, I tried it and still results in `undefined`. – dev404 Jun 30 '18 at 12:33
  • It's undefined then (immediately after setting the value in a callback), but if this is happening in an express routes it will be available the next time a route is called. – Robert Moskal Jun 30 '18 at 15:23
  • Yup, it is available in the views (one of the places I want to use the data), but it's not immediately available for the code in between the `find` and the `listen` sections. After checking the logs, the `listen` instruction kicks in before the query finishes, due to it being asynchronous. – dev404 Jun 30 '18 at 15:36

1 Answers1

1

Your node app will likely be comprised of route handlers for http requests. app.locals.section will be undefined if you call it outside of the callback, but it will exist in the route handler.

Let's say you were using something like express or restify:

const app = restify.createServer()

app.get('/', (req, res) => {
    return res.json(app.locals.sections)
})

Section.find().then(function(docs){
    app.locals.sections = docs
})

console.log(app.locals.section) // is undefined

app.listen(8080-, ()=>{
  console.log('Server started   ',8080)
})

Actually, it might be undefined if the database call took a long time and or a user hit the app super soon after startup. Starting the server in the callback would ensure app.locals.section existed under every scenario:

Section.find().then(function(docs){
    app.locals.sections = docs
    app.listen(8080-, ()=>{
      console.log('Server started   ',8080)
    })
})

You can use async/await within a function to make it seem like you aren't using promises. But you can't use it at the top level of your module. See here: How can I use async/await at the top level?

It really would be fairly idiomatic to do all your app startup in a promise chain. It's a style of coding you are going to see a lot of.

Section.find().then((docs)=>{app.locals.sections = docs})
    .then (()=>{/*dosomething with app.locals.sections */})
    .then(startServer)


function startServer() {app.listen(8080-, ()=>{
  console.log('Server started   ',8080)
})}
Robert Moskal
  • 21,737
  • 8
  • 62
  • 86
  • My only issue with a callback to start the server after the query is finished, is that I'd still want to add more code in between. Would it be possible to not start the server until everything else is finished? As it is right now it seems I'd have to chain every query/code into several callbacks 'til I'm finally finished. – dev404 Jun 30 '18 at 16:04
  • That's how it works, you could use the async/await pattern to make it seem like you weren't using callbacks. – Robert Moskal Jun 30 '18 at 16:19