-1

so I am having some issues with scopes when it comes to nodejs. I am wondering how you can initialize a variable in the global space, initialize it in a function (private scope) then have it equal to what you initialized it to in the private scope when you call it anywhere in the class. One thing I have noticed is that although you can use the variable in a private scope, when it is called again outside that function it just turns undefined. How can I return the same functions initialized when it was private? This is not my issued code but an example in case the picture isn't clear

let name;
class blah {
    static blablah() {
        name = josh;
    }
    console.log(name);
    //this will return undefined and not josh
}

What I need in my context:

let genesis;
let jsonChain;

class Blockchain {
    constructor() {
        //this.chain = [Blockchain.getGenesis()];
        console.log(Blockchain.getGenesis());
}
    //down the file we find the issued function...
    static getGenesis() {
         fs.readFile(jsonRoute, 'utf-8', function(err, data) {
             if (err) throw err;
    
             jsonChain = JSON.parse(data);
             genesis = jsonChain.blocks[0].GENESIS_DATA;
             return genesis;
        });
        //returning here instead inside the callback also yields undefined
        //I want to be able to access my contents from json file through 
        //the genesis variable which is global when I return this function
        //but I cannot reach the contents of the callback
    }
}

SOLUTION: Make sure to return the promise so that the async function runs later in your program when you call it.

let genesis;
let jsonChain;

class Blockchain {
    constructor() {
        this.chain = [Blockchain.getGenesis()];
            console.log(Blockchain.getGenesis().then(function(genesisData) {
      console.log(genesisData); // Genesis block data is here.
    }, function(err) {
      // This only runs if there was an error.
      console.log(err);
    }));
        }
    //down the file we find the solved function...
  static getGenesis() {
    return new Promise(function(resolve, reject) {
      fs.readFile(jsonRoute, 'utf-8', function(err, data) {
        if(err) return reject(err);
        const jsonChain = JSON.parse(data);
        resolve(jsonChain.blocks[0].GENESIS_DATA);
      });
    });
  }
    
}
module.exports = Blockchain;
  • You don't show anywhere that you actually call your `blablah()` method. And, your `console.log(name)` is trying to be inside the class definition, but not in a method which makes no sense. And, `josh` is not a defined value. – jfriend00 Jun 27 '20 at 17:23
  • We can see from the code you tried to stick in a comment below that the REAL problem is you're trying to update a higher scoped variable from an asynchronous callback. Besides being a bad design principle, you have no way of knowing from outside the callback WHEN the global has been updated. You are likely trying to access the global BEFORE it has been updated. That's just an anti-pattern anyway. Don't do it that way. Use the value from within the async callback OR call a function from within that callback and pass the value to that function. That's how you program asynchronously. – jfriend00 Jun 27 '20 at 17:26
  • Please show the code where you call `getGenesis()` and show the code where you try to use the `jsonChain` variable. That will illuminate the actual issue (as described in my previous comment) and will allow us to offer suggestions as to how you should do it. – jfriend00 Jun 27 '20 at 17:29
  • Also, don't ever, ever right `if (err) throw err` inside a plain asynchronous callback (that's another anti-pattern). That does nothing useful. You need real error handling there. – jfriend00 Jun 27 '20 at 17:30
  • thank you for the insight, I will need to come back later to fix this as I have mandatory work to do. But your insight is really appreciated I have learned alot! –  Jun 27 '20 at 17:33
  • Note: Here on this site, you should NOT put solutions into your answer. This site doesn't work that way. If you want to share your final solution, you can put it in an answer. Yes, you can answer your own question, though if you received help from others to get to that solution, you still often credit those who helped you with upvotes or accepted answer. – jfriend00 Jun 29 '20 at 04:46
  • Also, note you don't need to manually promisify `fs.readFile()` like you are doing. `fs.promises.readFile()` is bulit-in to current versions of node.js now and it already returns a promise. – jfriend00 Jun 29 '20 at 04:47

2 Answers2

2

Now that you've added the real code, there are a number of issues illustrated there.

  1. if (err) throw err inside a plain asynchronous callback does nothing useful. Nobody can catch that error and thus this should never be used. You need real error handling there. IMO, one should never write that line of code in that circumstance.

  2. return genesis; does nothing useful. That just returns back from the fs.readFile() callback where the internals of fs.readFile() are ignoring any return value. That does not return from the higher level function.

  3. jsonChain = JSON.parse(data); does successfully assign to the higher scoped variable, but any code that is trying to use the jsonChain variable will have no idea when it was assigned a value because fs.readFile() is asynchronous and calls its callback at some future, indeterminate time. So, if you're seeing that jsonChain has no value, that's because you're looking at the variable BEFORE it gets assigned. Basically, this is an anti-pattern. You can't program with asynchronous code that way in node.js. Instead, you need to USE the asynchronous value inside the callback or you need to call some function from within the callback and pass it the value. ONLY in that circumstance do you know WHEN the value is available.

  4. If the top level problem here is that you're trying to return some value you retrieved asynchronously, then you cannot directly do that in Javascript. You can read all about that here where it explains that you have to communicate that asynchronous value back to the caller with a callback, a promise or you can also use an event.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I have set the getGenesis() function to take a parameter named genChain, so in place for the genesis variable in the async function I set it to take in the value of it into getGenesis then return it and call it in constructor, this yielded undefined once again –  Jun 28 '20 at 05:12
  • 1
    @NoobGuy - You are apparently not understanding what a "non-blocking, asynchronous" operation means in node.js. It means that the rest of your code continues running and some time later at a time you have no idea when it will be, the asynchronous callback gets called. Meanwhile your function has long since returned BEFORE the asynchronous callback is called. Please read the link I put in my point #4 above. It explains how you communicate back an asynchronous value. There are only three options (callback, promise or event). You CANNOT return it. The function returns before the value is there. – jfriend00 Jun 28 '20 at 05:18
  • The issue was resolved, I had to simply return a promise. I edited the following code. Also thank you for your explanation, it aided my insight. –  Jun 29 '20 at 03:11
0

The first problem with your code is that josh is not in quotes. josh is undefined; did you mean 'josh' (in quotes)?


One way is to make your getGenesis() function async, so that you can wait for it to finish assigning the correct value to genesis. This requires wrapping everything inside an anonymous async function:

(async () => { 
  const fs = require('fs');
  let genesis;
  let jsonChain;

  class Blockchain {
    constructor() {
      this.chain = [Blockchain.getGenesis()];
      console.log(Blockchain.getGenesis());
    }

    static async getGenesis() {
      jsonChain = JSON.parse(await fs.promises.readFile(jsonRoute, 'utf-8'));
      genesis = jsonChain.blocks[0].GENESIS_DATA;
    }
  }

  console.log(genesis); // undefined D:
  await Blockchain.getGenesis();
  console.log(genesis); // not undefined :D
})();
GirkovArpa
  • 4,427
  • 4
  • 14
  • 43
  • 1
    assuming this is true, the context in which I am trying to use doesn't quite use quotes, I will load my context in a second for you to see what I did wrong and hopefully providing people like me who are new to node insight –  Jun 27 '20 at 17:15
  • ```class Blockchain { constructor() { this.chain = [Blockchain.getGenesis()]; console.log(Blockchain.getGenesis()); } //further down the file with the issued function static getGenesis() { fs.readFile(jsonRoute, 'utf-8', function(err, data) { if (err) throw err; jsonChain = JSON.parse(data); genesis = jsonChain.blocks[0].GENESIS_DATA; return genesis; }); //if I put return here it has the same undefined effect``` } –  Jun 27 '20 at 17:18
  • 1
    Consider updating your post; it's quite hard to read here in the comments. Even surrounded by single \` backticks. – GirkovArpa Jun 27 '20 at 17:19
  • ok GirkovArpa, I have added it to the question itself –  Jun 27 '20 at 17:24
  • wouldn't my constructor be undefined since it isnt after await though? –  Jun 28 '20 at 05:15
  • Your constructor is irrelevant since it's never called in your code, nor in my answer. Honestly you don't seem to understand much of anything in the code you're trying to write. Splitting up your question into several focused, individual ones would probably produce answers that are more helpful to you. – GirkovArpa Jun 28 '20 at 07:01
  • my constructor is called in the file that executes all my modules, so I need the returned array to be accessible to the entire file, in other words global. Also the array is exported to my other modules to be used so wouldnt this whole class be async and destroy itself before it reaches the other modules? I literally do not know much about async and await and I am new to nodejs which has very different properties to say c++ –  Jun 29 '20 at 02:37