从承诺结果导出节点模块

cas*_*raf 9 javascript node.js coffeescript

我正在尝试重写模块以返回与以前不同的值,但现在它使用异步调用来获取该值.(child_process如果重要的话).我把它包装在Promise中,但这对我来说并不重要 - 它可以在原始的child_process回调中,但问题是我无法将承诺链接到应用程序中的任何地方,因为我需要这个变为同步.这是我的模块:

exec = require('child_process').exec

platformHome = process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']

getExecPath = new Promise (resolve, reject) ->
  path = process.env.GEM_HOME

  if path
    resolve(path)
  else
    exec 'gem environment', (err, stdout, stderr) ->
      unless err
        line = stdout.split(/\r?\n/)
                 .find((l) -> ~l.indexOf('EXECUTABLE DIRECTORY'))
        if line
          resolve line[line.indexOf(': ') + 2..]
        else
          reject undefined

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
Run Code Online (Sandbox Code Playgroud)

显然,当需要模块时,这不起作用 - 如果我返回承诺本身,并then在之后使用require- 我的下一个module.exports将是异步,并且此链将继续.我该如何避免这种模式?

rsp*_*rsp 14

加载的Node中的模块require()是同步加载的,并且无法require返回异步加载的任何值.它可以返回一个承诺,但该模块的用户必须使用它作为:

require('module-name').then(value => {
  // you have your value here
});
Run Code Online (Sandbox Code Playgroud)

写不出来:

var value = require('module-name');
// you cannot have your value here because this line
// will get evaluated before that value is available
Run Code Online (Sandbox Code Playgroud)

当然,您可以在模块内部解析promise,并通过添加以下内容使其在导出的对象上设置属性:

module.exports = { GEM_HOME: null };
Run Code Online (Sandbox Code Playgroud)

并改变:

module.exports = GEM_HOME
Run Code Online (Sandbox Code Playgroud)

至:

module.exports.GEM_HOME = GEM_HOME
Run Code Online (Sandbox Code Playgroud)

在这种情况下,使用此模块的每个其他模块:

var x = require('module-name');
Run Code Online (Sandbox Code Playgroud)

x.GEM_HOME最初设置为,null但最终会在一段时间后更改为正确的值.但是它不会立即可用,因为require()在承诺之前的回报被解决并且值被设置.

目前正在讨论引入异步模块加载,其中包含可能适合您的用例的不同语法和语义.这是一个有争议的主题,值得阅读其背后的所有理由 - 请参阅:

有关详细信息,请参阅此答案: