0

In my express server, the root get request happens before an async function finishes listing the files from a database, but the get request needs information from the function to properly render the page. How can I have the get request wait until the other function is done?

I have looked at other solutions:

Request - Wait till API call is completed Node.js

How to get Express Node route to wait for function before rendering

Wait a async operation before execute render in express using NodeJs

But the answers don't seem universal enough for my problem.

async function listFiles() {

  artWorks = [];

  const [files] = await storage.bucket(bucketName).getFiles();

  files.forEach(file => {
    function splitStr(str) {
      var string = str.split("/");
      artWorks.push(
        {
          imgCategory: string[0],
          subCat: string[1].replace(/-/g, ' '),
          imgSrc: bucket + file.name,
          thumbnail: thumbBucket + file.name.split('.')[0] + "-thumb.jpg",
          alt: string[2]
        });
    }
    var str = file.name;
    splitStr(str);
  });
  console.log("files listed");
}


listFiles().catch(console.error);


app.get("/", function(req, res) {
  let randomArt = Math.floor(Math.random()*artWorks.length);
    setTimeout(() => {
      res.render("index", {
          randomArt: randomArt,
          artWorks: artWorks
        })
    }, 500);
});

You can see my current solution is to put the res.render inside a setTimeout in order to get it to wait, but I have set an arbitrary amount of time and sometimes it doesn't work. The page it renders "index" uses a randomly selected index of the artWorks array and if the array index is higher than 50 then the listFiles() function has not finished and it fails to load the home page saying:

TypeError: Cannot read property 'imgCategory' of undefined

Some notes:
The listFiles() function needs to remain independent of the get request as I call it in other parts of the code outside of the root get request.

The first time someone visits the site it will throw the error and not render the page, but upon refresh the artWorks array has finished and the page will render, I assume because it has cached the necessary information by that point.

So what is the best way to make the render wait while still keeping the function independent?

PsiKai
  • 1,803
  • 1
  • 5
  • 19

1 Answers1

1

Wait for the promise to resolve after it's called by changing:

listFiles().catch(console.error);

To:

listFiles()
  .then(() => {
    app.get("/", function(req, res) {
      let randomArt = Math.floor(Math.random()*artWorks.length);
      res.render("index", {
        randomArt: randomArt,
        artWorks: artWorks
      })
    });
  })
  .catch(console.error);
  • That's a nice solution, but I do need `listFiles()` to be independent of the get request. On another page the user will add to or delete from the database and it calls `listFiles()` for each of those circumstances. – PsiKai Oct 02 '20 at 20:53
  • Updated the answer. –  Oct 02 '20 at 20:55
  • Ok, I understand, thank you. It works in my dev environment. Will push to production and if it works I will accept your answer as correct. – PsiKai Oct 02 '20 at 21:05
  • Ok, I thought this was working, but after I clear my cache and attempt to access the home page it will say `Cannot GET /` but if I refresh the page the site works. Looks like this solution is still causing the page to render before `listFiles()` is done populating the array even with the `.then` Any ideas? – PsiKai Oct 03 '20 at 00:39
  • Are you making sure to add `app.listen` in the `then`? –  Oct 03 '20 at 00:41
  • Did you mean to say `app.get`? I don't see `app.listen` in your code example but I have `listen` down at the bottom of my code. I copied your code exactly. – PsiKai Oct 03 '20 at 00:43
  • I'd add `app.listen` in the `then`, because it doesn't make sense to let the user `GET /` when the server isn't finished loading yet. As for after you refresh the page, are you sure it's not showing it because it's finished loading? –  Oct 03 '20 at 00:44
  • It seems to me like the only reason it fails the first time and loads after refresh is because `listFiles()` is async, and the rest of the code is loaded before the `async` resolves. I assume it's caching the `artWorks` array and the next visit to the home page is successful because of the cache. This is my logic, but I could be wrong. – PsiKai Oct 03 '20 at 00:48
  • I thought that was your desired functionality? –  Oct 03 '20 at 00:49
  • No, I'm sorry if I didn't communicate well enough but the problem is that a person's first visit to the site results in an error which I'm sure I don't have to explain will kill the perception of the site... I'm looking to resolve this issue and have any new user of the site get access the first time. – PsiKai Oct 03 '20 at 00:52
  • It should have (before the code fix I posted) only errored out when you first load the server, if a user visits `/` before the artwork is loaded. Otherwise my guess is you have problems elsewhere. –  Oct 03 '20 at 00:56
  • Yes, that is/was the problem exactly. I wish I knew where else to look, it's a difficult problem to troubleshoot because in development it doesn't appear, but in production it does, but only on browser's first visit to the site. So anytime the site data gets redownloaded it fails on the first visit. – PsiKai Oct 03 '20 at 01:02
  • Right, it shouldn't be on anyone's first visit. It should be only if you just deployed the server and they get in a request in the first few milliseconds before it loads the artwork. I thought that was what you were trying to fix. Can you tell me what error you're getting? –  Oct 03 '20 at 01:04
  • Before your solution it was saying `Cannot read property 'imgCategory' of undefined` which is a value from the artworks array not yet loaded. Since your solution it only says `Cannot GET /` . Even upon redeploy of the server the issue doesn't happen so long as the browser I'm using has the site cached. – PsiKai Oct 03 '20 at 01:07
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/222431/discussion-between-l-lawliet-and-psikai). –  Oct 03 '20 at 01:08