Асинхронный экспорт модуля nodejs
мне было интересно, что лучший подход для настройки модуля экспорта. "асинхронный.функция " в приведенном ниже примере может быть запросом FS или HTTP, упрощенным ради примера:
вот пример кода (asynmodule.в JS):
var foo = "bar"
async.function(function(response) {
foo = "foobar";
// module.exports = foo; // having the export here breaks the app: foo is always undefined.
});
// having the export here results in working code, but without the variable being set.
module.exports = foo;
как экспортировать модуль только после выполнения асинхронного обратного вызова?
редактировать краткое замечание о моем фактическом использовании: я пишу модуль для настройки nconf (https://github.com/flatiron/nconf) в fs.существует () обратный вызов (т. е. он будет анализировать файл конфигурации и настраивать nconf).
6 ответов
ваш экспорт не может работать, потому что он находится вне функции в то время как foo
декларации находится внутри. Но если вы поместите экспорт внутрь, когда вы используете свой модуль, вы не можете быть уверены, что экспорт был определен.
лучший способ работы с системой ansync - использовать обратный вызов. Для получения обратного вызова необходимо экспортировать метод назначения обратного вызова и вызвать его при асинхронном выполнении.
пример:
var foo, callback;
async.function(function(response) {
foo = "foobar";
if( typeof callback == 'function' ){
callback(foo);
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback = cb;
}
}
здесь async.function
- это просто заполнитель для обозначения асинхронного вызова.
в основном
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
несколько обратный путь
если ваш модуль нужно вызывать более одного раза, вам нужно управлять массивом обратного вызова:
var foo, callbackList = [];
async.function(function(response) {
foo = "foobar";
// You can use all other form of array walk.
for(var i = 0; i < callbackList.length; i++){
callbackList[i](foo)
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback.push(cb);
}
}
здесь async.function
- это просто заполнитель для обозначения асинхронного вызова.
в основном
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
обещать путь
вы также можете использовать Promise для решения этой проблемы. Поддержка этого метода многократный вызов по дизайну обещания:
var foo, callback;
module.exports = new Promise(function(resolve, reject){
async.function(function(response) {
foo = "foobar"
resolve(foo);
});
});
здесь async.function
- это просто заполнитель для обозначения асинхронного вызова.
в основном
var fooMod = require('./foo.js').then(function(foo){
//Here code using foo;
});
посмотреть обещать документации
другим подходом было бы обертывание переменной внутри объекта.
var Wrapper = function(){
this.foo = "bar";
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
});
}
module.exports = new Wrapper();
Если инициализатор имеет ошибку, по крайней мере, вы все равно получаете неинициализированное значение вместо зависания обратного вызова.
ES6 ответ, используя обещания:
const asyncFunc = () => {
return new Promise((resolve, reject) => {
// Where someAsyncFunction takes a callback, i.e. api call
someAsyncFunction(data => {
resolve(data)
})
})
}
export default asyncFunc
...
import asyncFunc from './asyncFunc'
asyncFunc().then(data => { console.log(data) })
или вы можете вернуть само обещание напрямую:
const p = new Promise(...)
export default p
...
import p from './asyncModule'
p.then(...)
вы также можете использовать обещания:
some-async-module.js
module.exports = new Promise((resolve, reject) => {
setTimeout(resolve.bind(null, 'someValueToBeReturned'), 2000);
});
main.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// outputs 'someValueToBeReturned' after 2 seconds
то же самое может произойти в другом модуле и также разрешится, как ожидалось:
in-some-other-module.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// also outputs 'someValueToBeReturned' after 2 seconds
обратите внимание, что объект promise создается один раз, а затем кэшируется узлом. Каждый require('./some-async-module')
вернет тот же экземпляр объекта (экземпляр promise в этом случае.)
подход ES7 будет немедленно вызывается функция async в модуле.экспорт :
module.exports = (async function(){
//some async initiallizers
//e.g. await the db module that has the same structure like this
var db = await require("./db");
var foo = "bar";
//resolve the export promise
return {
foo
};
})()
Это может потребоваться с await позже:
(async function(){
var foo = await require("./theuppercode");
console.log(foo);
})();
другие ответы, казалось, были частичными ответами и не работали для меня. Это кажется несколько полным:
некоторые-модуль.js
var Wrapper = function(){
this.callbacks = [];
this.foo = null;
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
this.callbacks.forEach(function(callback){
callback(null, wrapper.foo);
});
});
}
Wrapper.prototype.get = function(cb) {
if(typeof cb !== 'function') {
return this.connection; // this could be null so probably just throw
}
if(this.foo) {
return cb(null, this.foo);
}
this.callbacks.push(cb);
}
module.exports = new Wrapper();
main.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined
});
main2.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined in another script
});