最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Exporting Node module from promise result - Stack Overflow

programmeradmin0浏览0评论

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?

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?

Share Improve this question edited Aug 2, 2022 at 16:02 casraf asked Dec 22, 2016 at 9:29 casrafcasraf 21.7k10 gold badges59 silver badges93 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 15

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?

Node.js modules are loaded synchronously.

You can deal with this exporting the Promise value.

#@ your module.js
module.exports = new Promise()

and:

#@ your app.js
const mod = require('./module');

mod.then((value => ...);
发布评论

评论列表(0)

  1. 暂无评论