web-dev-qa-db-fra.com

Exportation du module nodejs asynchrone

Je me demandais quelle était la meilleure approche pour configurer un export de module. "async.function" dans l'exemple ci-dessous pourrait être une requête FS ou HTTP, simplifiée pour l'exemple:

Voici un exemple de code (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;

Comment puis-je exporter le module uniquement une fois que le rappel async a été exécuté? 

edit une note rapide sur mon cas d'utilisation actuel: j'écris un module pour configurer nconf ( https://github.com/flatiron/nconf ) dans un fichier fs.exists ( ) callback (c’est-à-dire qu’il analysera un fichier de configuration et configurera nconf). 

48
Brett

Votre exportation ne peut pas fonctionner car elle est en dehors de la fonction alors que la foodeclaration est à l'intérieur. Mais si vous insérez l'exportation à l'intérieur, lorsque vous utilisez votre module, vous ne pouvez pas être sûr que l'exportation a été définie.

La meilleure façon de travailler avec un système Ansync consiste à utiliser le rappel. Vous devez exporter une méthode d'attribution de rappel pour obtenir le rappel et l'appeler lors de l'exécution asynchrone.

Exemple:

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;
    }
}

Here async.function est simplement un espace réservé pour symboliser un appel asynchrone.

En principale

var fooMod = require('./foo.js');
fooMod(function(foo){
    //Here code using foo;
});

Manière de rappel multiple

Si votre module doit être appelé plus d'une fois, vous devez gérer un tableau de callback:

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);
    }
}

Here async.function est simplement un espace réservé pour symboliser un appel asynchrone.

En principale

var fooMod = require('./foo.js');
fooMod(function(foo){
    //Here code using foo;
});

Chemin de promesse

Vous pouvez également utiliser Promise pour résoudre ce problème. Cette méthode prend en charge plusieurs appels de la conception de la promesse:

var foo, callback;
module.exports = new Promise(function(resolve, reject){
    async.function(function(response) {
        foo = "foobar"

        resolve(foo);
    });
});

Here async.function est simplement un espace réservé pour symboliser un appel asynchrone.

En principale

var fooMod = require('./foo.js').then(function(foo){
    //Here code using foo;
});

Voir Documentation Promise

47
Techniv

ES6 répond en utilisant des promesses:

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) })

Ou vous pouvez retourner la promesse elle-même directement:

const p = new Promise(...)
export default p
...
import p from './asyncModule'
p.then(...)
10
inostia

Une autre approche consisterait à envelopper la variable dans un objet.

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();

Si l'initialiseur a une erreur, au moins vous obtenez toujours la valeur non initialisée au lieu du rappel en attente.

10
vangoz

Vous pouvez également utiliser Promises:

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

La même chose peut arriver dans un module différent et va également résoudre comme prévu:

in-some-other-module.js

var asyncModule = require('./some-async-module');

asyncModule.then(promisedResult => console.log(promisedResult)); 
// also outputs 'someValueToBeReturned' after 2 seconds

Notez que l'objet de promesse est créé une fois puis est mis en cache par noeud. Chaque require('./some-async-module') retournera la même instance d'objet (instance de promesse dans ce cas).

8
efidiles

Une approche ES7 serait une fonction asynchrone invoquée immédiatement dans module.exports

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
  };
})()

Cela peut être demandé avec wait plus tard:

(async function(){

  var foo = await require("./theuppercode");
  console.log(foo);
})();
6
Jonas Wilms

Les autres réponses semblaient être des réponses partielles et ne fonctionnaient pas pour moi. Cela semble être un peu complet:

some-module.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
});
0
tsuz