Экспорт модуля Node из результата обещания

Я пытаюсь переписать модуль, чтобы вернуть другое значение, чем раньше, но теперь он использует асинхронный вызов для получения этого значения. (с child_process если это имеет значение). Я завернул его в обещание, но это не критично для меня - оно может быть в исходном обратном вызове 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

Очевидно, что при требовании модуля это не сработает - и если я верну свое обещание и использую then после require, мой следующий module.exports будет асинхронным, и эта цепочка будет продолжена. Как избежать этого шаблона?

Ответ 1

Модули в Node, загруженные с помощью require(), загружаются синхронно, и невозможно, чтобы require возвращало любое значение, которое загружается асинхронно. Он может вернуть обещание, но пользователи этого модуля должны будут использовать его как:

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

Невозможно было бы написать:

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

Конечно, вы можете иметь обещание, разрешенное внутри вашего модуля, и настроить его на экспортируемый объект, добавив что-то вроде этого:

module.exports = { GEM_HOME: null };

и изменения:

module.exports = GEM_HOME

в

module.exports.GEM_HOME = GEM_HOME

В этом случае каждый другой модуль, который использует этот модуль как:

var x = require('module-name');

будет иметь x.GEM_HOME, первоначально установленный в null, но позже он будет изменен на правильное значение. Он был бы недоступен сразу же, потому что require() возвращается до того, как обещание будет установлено, и значение будет установлено.

Продолжается дискуссия по внедрению асинхронной загрузки модуля с различным синтаксисом и семантикой, которые могут быть пригодны для вашего использования. Это противоречивые сюжеты, и стоит прочитать все обоснования, стоящие за ним - см.:

См. также этот ответ для более подробной информации:

Ответ 2

Node.js загружаются синхронно.

Вы можете справиться с этим экспортом значения Promise.

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

и

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

mod.then((value => ...);