Export variable - ExpressJS

29 views
Skip to first unread message

Peter B.

unread,
May 3, 2017, 5:55:59 PM5/3/17
to nodejs
Hi everyone,

I need your help. I'm working on a project and I have a problem with the fact that nodejs is asynchronous.
I want to export my variable called 'macAd'. But even with a global variable, I can't do it. 

Can you help me please ?

Here is my code (I use ExpressJS Framework) :













I have this output : 






















Thank you.

Zlatko

unread,
May 4, 2017, 9:53:23 PM5/4/17
to nodejs

On Wednesday, May 3, 2017 at 11:55:59 PM UTC+2, Peter B. wrote:

I need your help. I'm working on a project and I have a problem with the fact that nodejs is asynchronous.
I want to export my variable called 'macAd'. But even with a global variable, I can't do it. 


If I understood correctly, you want to export macAd? But later you don't see this variable? Well, it might take a few words to explain, but it's a simple thing really.
The problem is if you do something like:

let macAd; // undefined
module.exports  = macAd; // again, undefined
macAd = 3; // doesn't matter now, because the exports is bound to undefined already

if you require it later, you'll get undefined, not a reference to your variable. It's passed by value.

But if you export an object instead, you can modify that object later (say, by setting a macAd property), without changing the reference to the object - which means whoever required the module will get to resolve the value after you set it.

Have your module set up like this:

const macAd = require('macad')
const obj = {
  macAd: undefined,
};
module.exports = obj;
// Now the export has a firm grip on our object. EVen if the value is undefined itself, the object is there and it'll be
// useful later.
console.log(obj); // -> { macAd: undefined }; 

macAd.getMac(function (err, mac) {
  // obviously handle error first.
  obj.macAd = mac;
  console.log(obj); // -> { macAd: '::1' };
});




Then, when you require the module later, at first it's going to be undefined, but later it should resolve to your value. E.g.

const myModule = require('./whatever-you-named-it');
const TIMEOUT = 1000; // or however long the 'macad' module takes.
console.log(myModule.macAd); // -> undefined
setTimeout(function () {
  console.log(myModule.macAd); // -> should give you ::1 or something.
}, TIMEOUT);

Now you have a problem though, your module is async, but you're not sure when is it complete and when it's safe to use it. That TIMEOUT might be either too much for you or maybe too short if the computer is busy.

You can get around that by wrapping the getMac call in an initializer function, like this:

const macAd = require('macad')
const obj = {
  macAd: undefined,
  initialize: function () {
    // I use a promise but yuou can go with a callback too
    return new Promise(function(resolve, reject) { 
      macAd.getMac(function (err, mac) {
        if (err) {
          return reject(err);
        obj.macAd = mac;
        resolve();
      });
    });
  }
};
module.exports = obj;
console.log(obj); // { macAd: undefined, initialize: [Function] }

Now you can require a module, and call initialize on it, and you'll get a promise resolved (or rejected) when the macAd is available, like this:

const myModule = require('myModule');
console.log(myModule.macAd); // most likely undefined
myModule.initialize()
.then(function() {
  // now myModule.macAd is available.
  console.log(myModule.macAd); // ::1
})
.catch(console.error);

But here we have other issues. For one, you might simply call getMac directly elsewhere like this, there's no direct benefit in wrapping it in its own module. And another thing - every time you call your module, the getMac is going to be resolving. In this particular case it might not be a big issue, but what if it wasn't a getMac? What if it was something calling an expensive 3rd party service? You call it every time you call this.

That's why you basically store a reference to that promise, and only call the full init once - every other time you return what you already have.

const macAd = require('macad')
let initPromise; // by keeping it out of obj, you're keeping it private to the module itself.
const obj = {
  macAd: undefined,
  initialize: function () {
    // first check if another module already called init - then just resolve what we have, they don't have to wait for getMac
    if (initPromise) {
      return initPromise;
    }
    // if it wasn't already called, call macAd now and store the resulting promise in initPromise.
    initPromise = new Promise(function (resolve, reject) {
      macAd.getMac(function (err, mac) {
        if (err) {
          return reject(err);
        }
        obj.macAd = mac;
      });
    });
    return initPromise;
  }
};
module.exports = obj;

Hope this helps you out a bit.

Ethan Garofolo

unread,
May 4, 2017, 9:53:23 PM5/4/17
to nodejs
There are a couple of ways around this.  I don't know your familiarity with Node.js, so if something doesn't make sense, please ask follow up questions.

The skinny of it is, whatever needs to use that macAddress variables needs to wait for it to come back.  There's really no way around that.  So rather than trying to get the macAddress value available outside of your callback there, whatever needs to use the macAddress would need to use it inside of that callback (as you observed in your "In the function" console statement).

Another option would be to use a promise.  Are you familiar with those?  You'd end up with something like:

macAddressPromise = new Promise(function (resolve, reject) {
  macGetting.getMac(function (err, macAddress) {
    if (err) return reject(err)

    return resolve(macAddress)
  })
})

macAddressPromise is now a variable that you can pass around to wherever.  To then actually get the address itself instead of a Promise for the address, you'd just call:

macAddressPromise.then(function (macAddress) {
  // inside of here you have access to the actual address
})

Am I explaining this well?
Reply all
Reply to author
Forward
0 new messages