Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exporting Node module from promise result

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?

like image 809
casraf Avatar asked Dec 22 '16 09:12

casraf


People also ask

How to export a promise result from a module?

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;

How do I export from a node module?

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:

What is the difference between module exports and exports in JavaScript?

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,...

How to expose different types as a module in NodeJS?

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.


1 Answers

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:

  • Node.js, TC-39, and Modules by James M Snell from iBM
  • ES6 Module Interoperability - Node.js Enhancement Proposals
  • In Defense of .js - A Proposal for Node.js Modules by Dave Herman, Yehuda Katz and Caridy Patiño
  • Discussion on the Pull Request #3 of node-eps (002: ES6 module interop)

See also this answer for more details:

  • javascript - Why is there a spec for sync and async modules?
like image 95
rsp Avatar answered Oct 09 '22 03:10

rsp