I'm trying to rewrite a module to return a different value than before, but now it uses an async call to get that value. (with child_process
if it matters). I've wrapped it in a Promise, but that's not critical to me - it can be in the original child_process callback, but the problem is I can't chain the promise to everywhere in the app because I need this to become synchronous. Here's my module:
const { exec } = require("child_process");
const platformHome = process.env[process.platform === "win32" ? "USERPROFILE" : "HOME"];
const getExecPath = new Promise((resolve, reject) => {
const path = process.env.GEM_HOME;
if (path) {
resolve(path);
return;
}
exec("gem environment", (err, stdout, err) => {
if (err) {
reject(err);
}
const line = stdout.split(/\r?\n/).find(l => ~l.indexOf("EXECUTABLE DIRECTORY"));
if (line) {
resolve(line.substring(line.indexOf(": ") + 2));
} else {
reject(undefined);
}
});
});
let GEM_HOME = undefined;
getExecPath
.then(path => (GEM_HOME = path))
.catch(() => (GEM_HOME = `${platformHome}/.gem/ruby/2.3.0`))
.then(
() => (module.exports = GEM_HOME) // or simply return it
);
Clearly, when requiring the module, this doesn't work - and if I return the promise itself, and use then
after require
- my next module.exports
will be async, and this chain will carry on. How do I avoid this pattern?
You can export your promise result by simply resolving the promise in the module from which to want to export, and in then () block use exports.variable_name = promiseResult;
There's another way of exporting from a Node.js module called "named export". Instead of assigning the whole module.exports to a value, we would assign individual properties of the default module.exports object to values. Something like this:
The module.exports or exports is a special object which is included in every JS file in the Node.js application by default. module is a variable that represents current module and exports is an object that will be exposed as a module. So, whatever you assign to module.exports or exports,...
Here, you will learn how to expose different types as a module using module.exports. The module.exports is a special object which is included in every JavaScript file in the Node.js application by default. The module is a variable that represents the current module, and exports is an object that will be exposed as a module.
Modules in Node that you load with require()
are loaded synchronously and it is not possible for require
to return any value that is loaded asynchronously. It can return a promise but then users of that module would have to use it as:
require('module-name').then(value => {
// you have your value here
});
It would not be possible to write:
var value = require('module-name');
// you cannot have your value here because this line
// will get evaluated before that value is available
Of course you can have the promise resolved inside of your module and make it set a property on the exported object by adding something like this:
module.exports = { GEM_HOME: null };
and changing:
module.exports = GEM_HOME
to:
module.exports.GEM_HOME = GEM_HOME
In that case, every other module that uses this module as:
var x = require('module-name');
will have x.GEM_HOME
originally set to null
but it would eventually get changed to a correct value some time later. It would not be available right away though, because require()
returns before the promise is settled and the value is set.
There is an ongoing discussion to introduce asynchronous module loading with different syntax and semantics that may be suited for your use case. It's a controversial subjects and it's worth reading all of the rationale behind it - see:
See also this answer for more details:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With