Экспорт модуля 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, TC-39 и модули от Джеймса М Снелла от iBM
- Совместимость модуля ES6 - предложения по расширению Node.js
- В защиту.js - предложение для модулей Node.js от Дэйва Хермана, Иегуды Каца и Кариди Патиньо
- Обсуждение запроса на извлечение № 3 узла-eps (002: взаимодействие модуля ES6)
Смотрите также этот ответ для более подробной информации:
Модули Node.js загружаются синхронно.
Вы можете справиться с этим, экспортируя значение Promise.
#@ your module.js
module.exports = new Promise()
а также:
#@ your app.js
const mod = require('./module');
mod.then((value => ...);