Экспорт модуля 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 будет асинхронным, и эта цепь будет продолжаться. Как мне избежать этого паттерна?

2 ответа

Модули в узле, которые вы загружаете 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() возвращается до того, как обещание выполнено и значение установлено.

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

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

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

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

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

а также:

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

mod.then((value => ...);
Другие вопросы по тегам