Domanda

In Nodejs non ci sono praticamente operazioni di I / O bloccanti. Ciò significa che quasi tutto il codice IO nodejs comporta molti callback. Questo vale per la lettura e la scrittura su / da database, file, processi, ecc. Un tipico esempio di ciò è il seguente:

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

Sto anticipando la scrittura di codice che renderà molte operazioni IO, quindi mi aspetto di scrivere molti callback. Sono abbastanza a mio agio con l'utilizzo dei callback, ma sono preoccupato per tutta la ricorsione. Sono in pericolo di imbattermi in troppe ricorsioni e soffiare attraverso una pila da qualche parte? Se eseguo migliaia di scritture individuali nel mio archivio di valori-chiave con migliaia di callback, il mio programma alla fine si bloccherà?

Sto fraintendendo o sottovalutando l'impatto? In caso contrario, c'è un modo per aggirare questo mentre si utilizza ancora lo stile di codifica di callback di Nodejs?

È stato utile?

Soluzione

Nessuno dei codici che mostri sta usando la ricorsione. Quando chiami useFile , chiama posix.stat () , che ritorna, e useFile termina quando è terminato. In un secondo momento, quando la chiamata a posix.stat () ha completato all'interno del sistema sottostante e i risultati sono disponibili, verrà eseguita la funzione di callback aggiunta per quella . Che chiama posix.open () , quindi termina quando è terminato. Una volta che il file è stato aperto con successo, la funzione di callback per che verrà eseguita, chiamando posix.read () , e quindi terminerà mentre anch'essa è stata completata . Infine, quando saranno disponibili i risultati della lettura, verrà eseguita la funzione più interna.

Il punto importante è che ogni funzione viene eseguita fino al completamento, poiché le chiamate alle funzioni posix. * () non sono bloccanti: cioè, ritornano immediatamente, dopo aver causato un po 'di magia iniziato nel sistema sottostante. Quindi ciascuna delle tue funzioni termina e in seguito un evento farà eseguire la funzione successiva; ma non c'è mai alcuna ricorsione.

La struttura nidificata del codice può dare l'impressione che le cose all'interno dovranno finire prima che le cose esterne possano arrivare al proprio punto finale. Ma in questo stile di programmazione asincrona guidata dagli eventi ha più senso vedere l'annidamento in termini di più profondo = > accade-tardi-che .

EDIT: prova ad aggiungere alcune istruzioni di registrazione immediatamente prima della fine di ogni funzione nidificata; questo aiuterà a dimostrare che l'ordine in cui completano è dall'esterno verso l'interno.

Altri suggerimenti

Stesso esempio, con l'output di debug aggiunto (vedi sotto per l'output):

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

Output:

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

Puoi provare

http://github.com/creationix/do

o ruota il tuo come ho fatto io. Non importa per ora la gestione degli errori mancanti (ignoralo);)

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

E un semplice esempio che lo utilizza

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

Le tue cose vanno bene. Eseguo chiamate ricorsive in Express per seguire i reindirizzamenti HTTP, ma ciò che fai è "attraversare". e non ricorsione

Dai un'occhiata anche a 'step' ( http://github.com/creationix/step) o 'flow-js' su github. Ciò consente di scrivere flussi di callback in uno stile più naturale. Ciò chiarirà inoltre che non è in corso alcuna ricorsione.

Come con qualsiasi JavaScript, è possibile effettuare chiamate ricorsive con Node.js. Se riscontri problemi di profondità di ricorsione (come sottolinea NickFitz, non sembri essere in pericolo per questo), puoi spesso riscrivere il tuo codice per utilizzare invece un timer intervallo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top