Асинхронный экспорт модуля 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
});