0

I need some help to understand the reason of why after a forEach my const is getting empty. I read and find some questions but none of them help me understand. I believe that is happening because JS is asynchronous, but I can't figure out how to solve myself.

So, the code is very simple I have a NodeJS API that will connect to more then one database, and return all the info. I am using pg-promise to connect to PostgreSQL.

export default class AllInfo {
  constructor(databases) {
    this.databases = databases;
    this.options = {
      promiseLib: promise,
    };
    this.databaseConnection = new Pg(this.options);
  }

And after that, the trick method:

 getAllInformation() {
    const entidades = [];
    this.databases.getStringConnection().forEach((db) => {
      const connection = this.databaseConnection(db);
      connection.any('SELECT * FROM information').then((data) => {
        entidades.push(data);
      });
      connection.$pool.end();
    });
    return entidades;
  } 

In this code, my return is always empty ([]) when it is requested.

If I log the const entidades inside the loop, the information is logged successfully. But if I log after the loop and before the return, it is empty.

 getAllInformation() {
    const entidades = [];
    this.databases.getStringConnection().forEach((db) => {
      const connection = this.databaseConnection(db);
      connection.any('SELECT * FROM information').then((data) => {
        entidades.push(data);
        console.log(entidades) // here it works
      });
      connection.$pool.end();
    });
    return entidades;
  } 

And if I try to log outside:

 getAllInformation() {
    const entidades = [];
    this.databases.getStringConnection().forEach((db) => {
      const connection = this.databaseConnection(db);
      connection.any('SELECT * FROM information').then((data) => {
        entidades.push(data);

      });
      connection.$pool.end();
    });
    console.log(entidades) // here doesn't work
    return entidades;
  }

Someone can explain why this happens and where I look for the solution?

Francisco
  • 1,307
  • 2
  • 8
  • 7
  • i also think its an asynchronous problem, try using promise or async/await – Chris Li Sep 13 '18 at 15:53
  • Why do you want `const`? Should `const` change? Maybe you want `var`? – Bunyk Sep 13 '18 at 15:53
  • 1
    "*Using a forEach to create another array with the push method…*" is totally wrong anyway. That's what `.map` was made for. – Bergi Sep 13 '18 at 16:08
  • Since you're using promises already, have a look at `Promise.all` – Bergi Sep 13 '18 at 16:10
  • @Bunyk a `const` doesn't change its *value* but for objects (and an array is an object) that means reassigning to a *different* object. For example `const myObj = {name: "alice" };` followed by `myObj = {name: "bob"}`. However, `myObj.name = "bob"` is valid. Besides, if you don't want a `const`, then it's better to use `let` rather than `var`. There are very few reasons you'd want a `var` if you can just use the ES6 keywords. – VLAZ Sep 13 '18 at 16:23
  • 1
    @Bunyk complementing the explanation of vlaz if I declare with let the Lint throws me: " 'entidades' is never reassigned. Use 'const' instead. (prefer-const)". – Francisco Sep 13 '18 at 16:43
  • @vlaz I dind't find this question before. Maybe It is because is Ajax referenced. Thank you there is good information there. – Francisco Sep 13 '18 at 16:47
  • Also, initializing the connection and then destroying the connection pool inside the method looks very wrong. It is not how you use `pg-promise`. See: [Where to initialize pg-promise](https://stackoverflow.com/questions/34382796/where-should-i-initialize-pg-promise). – vitaly-t Sep 15 '18 at 04:35
  • Hey @vitaly-t it is a honor. I am using to connect in more then one database and execute the select. So if I do close the connection I get a warning in the console. Also this connection is not used anywhere else. Think like a multi tenant app, where you want to perform querys in differents databases. I don't have how to keep the connection open for the next request, if I dont't know wich database goes. Have you use like this? Any thoughts? – Francisco Sep 16 '18 at 14:49
  • @Francisco Simple - you create one `db` for each connection, and then reuse those. And if you have dynamic list, there must be a unique key that you can put into hash and then pull the right `db` object from there when needed. – vitaly-t Sep 16 '18 at 19:22
  • @vitaly-t, thanks I will do that. Appreciate the help. – Francisco Sep 19 '18 at 19:52

2 Answers2

1

It is as you think. It is returning an empty array because JS is asynchronous and you are returning the data as if it was synchronous.

You can push the promises of connection.any('SELECT * FROM information') into the array instead of pushing the result, thus, you can wait until all the promises are resolved/rejected to continue.

try this:

function getAllInformation() {
    const entidades = [];
    var entidadesPromises = [];
    this.databases.getStringConnection().forEach((db) => {
        const connection = this.databaseConnection(db);
        entidadesPromises.push(connection.any('SELECT * FROM information'));
        connection.$pool.end();
    });
    return Promise.all(entidadesPromises).then((data) => {
        entidades.push(data);
        console.log(entidades) // here it works
        return entidades;
    });
} 

getAllInformation().then(entidades => {
    // Entidades will be an array containing the data retrieved from the databases.
});
0

connection.any() is returning a promise and executes the anonymous function that pushes the data to your array AFTER the promise is resolved. This is why the anonymous function is executed asynchronously. However you could wait for the data to be returned by the any function like this:

let data = await connection.any('SELECT * FROM information');
entidades.push(data);
D0miH
  • 122
  • 5
  • Given the OP is still using `forEach`, placing an `await` there won't help - that's equivalent to the `.then()` call they are currently using – Bergi Sep 13 '18 at 16:09
  • Hey @Psycho, thanks for trying to help. If I change for this code, the project doesn't compile. The error: "await is a reserved word". – Francisco Sep 13 '18 at 16:49