Question

Dans Nodejs, il n’existe pratiquement aucune opération d’E / S bloquante. Cela signifie que presque tout le code IO de nodejs implique de nombreux rappels. Ceci s’applique à la lecture et à l’écriture sur / à partir de bases de données, fichiers, processus, etc. Voici un exemple typique:

var useFile = function(filename,callback){
    posix.stat(filename).addCallback(function (stats) {
        posix.open(filename, process.O_RDONLY, 0666).addCallback(function (fd) {
            posix.read(fd, stats.size, 0).addCallback(function(contents){
                callback(contents);
            });
        });
    });
};

...

useFile("test.data",function(data){
    // use data..
});

Je prévois écrire un code qui générera beaucoup d'opérations d'E / S. Je m'attends donc à écrire de nombreux rappels. Je suis assez à l'aise avec l'utilisation des rappels, mais je suis inquiet de toute la récursivité. Est-ce que je risque de rencontrer trop de récursion et de souffler quelque part dans une pile? Si je fais des milliers d'écritures individuelles dans mon magasin de valeurs-clés avec des milliers de rappels, mon programme va-t-il finir par planter?

Est-ce que je comprends mal ou sous-estime l'impact? Sinon, est-il possible de contourner ce problème tout en utilisant le style de code de rappel de Nodejs?

Était-ce utile?

La solution

Aucun des codes présentés n’utilise la récursivité. Lorsque vous appelez useFile , il appelle posix.stat () , qui renvoie, et useFile se termine lorsqu'il a été terminé. Plus tard, lorsque l'appel à posix.stat () est terminé dans le système sous-jacent et que les résultats sont disponibles, la fonction de rappel que vous avez ajoutée pour cela sera exécutée. . Cela appelle posix.open () , puis se termine au terme de son exécution. Une fois que le fichier a été ouvert avec succès, la fonction de rappel pour que va exécuter, en appelant posix.read () , et se terminera ensuite car elle aussi a fonctionné jusqu'au bout. . Enfin, lorsque les résultats de la lecture sont disponibles, la fonction la plus interne est exécutée.

Le point important est que chaque fonction est entièrement exécutée, car les appels aux fonctions posix. * () ne sont pas bloquants: en d’autres termes, ils reviennent immédiatement, ce qui a provoqué une certaine magie. commencé dans le système sous-jacent. Ainsi, chacune de vos fonctions se termine et, plus tard, un événement entraîne l'exécution de la fonction suivante; mais à aucun moment il n'y a de récursivité.

La structure imbriquée du code peut donner l’impression que le contenu interne doit être terminé avant que le contenu externe puisse atteindre son propre point de terminaison. Mais dans ce style de programmation événementielle asynchrone, il est plus logique de voir l'imbrication en termes de deeper = > se passe plus tard que .

EDIT: essayez d’ajouter des instructions de journalisation juste avant la fin de chaque fonction imbriquée; cela aidera à illustrer le fait que l'ordre dans lequel elles sont accomplies est de l'extérieur vers l'intérieur.

Autres conseils

Même exemple, avec une sortie de débogage ajoutée (voir ci-dessous pour la sortie):

usefile.js:

var sys = require("sys"),
  posix = require("posix");

var useFile = function(filename,callback){
    posix.stat(filename).addCallback(function (stats) {
        posix.open(filename, process.O_RDONLY, 0666).addCallback(function (fd) {
            posix.read(fd, stats.size, 0).addCallback(function(contents){
                callback(contents);
                sys.debug("useFile callback returned");
            });
            sys.debug("read returned");
        });
        sys.debug("open returned");
    });
    sys.debug("stat returned");
};

useFile("usefile.js",function(){});

Sortie:

DEBUG: stat returned
DEBUG: open returned
DEBUG: read returned
DEBUG: useFile callback returned

Vous pouvez essayer

http://github.com/creationix/do

ou roulez le vôtre comme je l'ai fait. Peu importe la gestion des erreurs pour le moment (ignorez simplement cela);)

var sys = require('sys');

var Simplifier = exports.Simplifier = function() {}

Simplifier.prototype.execute = function(context, functions, finalFunction) {
  this.functions = functions;
  this.results = {};
  this.finalFunction = finalFunction;
  this.totalNumberOfCallbacks = 0
  this.context = context;
  var self = this;

  functions.forEach(function(f) {
    f(function() {
      self.totalNumberOfCallbacks = self.totalNumberOfCallbacks + 1;
      self.results[f] = Array.prototype.slice.call(arguments, 0);     
      if(self.totalNumberOfCallbacks >= self.functions.length) {
        // Order the results by the calling order of the functions
        var finalResults = [];
        self.functions.forEach(function(f) {
          finalResults.push(self.results[f][0]);
        })
        // Call the final function passing back all the collected results in the right order 
        finalFunction.apply(self.context, finalResults);
      }
    });
  });
}

Et un exemple simple d'utilisation

// Execute 
new simplifier.Simplifier().execute(
  // Context of execution
  self,  
  // Array of processes to execute before doing final handling
  [function(callback) {
      db.collection('githubusers', function(err, collection) {
        collection.find({}, {limit:30}, function(err, cursor) {
          cursor.toArray(function(err, users) { callback(users); })
        });
      });      
    },

    function(callback) {
      db.collection('githubprojects', function(err, collection) {
        collection.find({}, {limit:45, sort:[['watchers', -1]]}, function(err, cursor) {
          cursor.toArray(function(err, projects) { callback(projects); })
        });
      });              
    }
  ],  
  // Handle the final result
  function(users, projects) {
    // Do something when ready
  }
);

Vos affaires vont bien. Je fais des appels récursifs dans Express pour suivre les redirections HTTP, mais ce que vous faites est de "traverser". et non la récursivité

Jetez également un coup d'oeil à 'step' ( http://github.com/creationix/step) ou 'flow-js' sur github. Cela vous permet d'écrire des flux de rappel dans un style plus naturel. Cela indiquera également clairement qu'il n'y a pas de récursivité.

Comme avec tout JavaScript, il est possible de faire des appels récursifs avec Node.js. Si vous rencontrez des problèmes de profondeur de récursivité (comme le fait remarquer NickFitz, vous ne semblez pas être en danger), vous pouvez souvent réécrire votre code pour utiliser un minuteur d'intervalle.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top