Pregunta

En Nodejs, prácticamente no hay operaciones de bloqueo de E / S. Esto significa que casi todo el código de IO de nodejs implica muchas devoluciones de llamada. Esto se aplica a la lectura y escritura a / desde bases de datos, archivos, procesos, etc. Un ejemplo típico de esto es el siguiente:

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

Estoy anticipando escribir código que hará muchas operaciones de IO, por lo que espero estar escribiendo muchas devoluciones de llamada. Estoy bastante cómodo con el uso de devoluciones de llamada, pero estoy preocupado por todas las recursiones. ¿Estoy en peligro de sufrir demasiada recursión y soplar a través de una pila en alguna parte? Si realizo miles de escrituras individuales en mi almacén de valor clave con miles de devoluciones de llamada, ¿se bloqueará mi programa?

¿Estoy malinterpretando o subestimando el impacto? Si no es así, ¿hay alguna forma de evitar esto mientras aún usas el estilo de codificación de devolución de llamadas de Nodejs?

¿Fue útil?

Solución

Ninguno de los códigos que muestra está usando recursión. Cuando llama a useFile , llama a posix.stat () , que devuelve, y useFile termina a medida que se ejecuta. En algún momento posterior, cuando la llamada a posix.stat () haya completado dentro del sistema subyacente y los resultados estén disponibles, se ejecutará la función de devolución de llamada que agregó. . Eso llama a posix.open () , y luego termina a medida que se ejecuta hasta su finalización. Una vez que el archivo se ha abierto con éxito, la función de devolución de llamada para que se ejecutará, llamando a posix.read () , y luego terminará, ya que también se ha completado. . Finalmente, cuando los resultados de la lectura estén disponibles, se ejecutará la función más interna.

El punto importante es que cada función se ejecuta hasta que se completa, ya que las llamadas a las funciones posix. * () no se bloquean: es decir, regresan de inmediato, lo que ha provocado cierta magia. Comenzó en el sistema subyacente. Entonces, cada una de tus funciones termina, y luego un evento hará que la siguiente función se ejecute; pero en ningún momento hay recursión.

La estructura anidada del código puede dar la impresión de que las cosas que hay dentro deben terminar antes de que las cosas que están fuera puedan llegar a su propio punto final. Pero en este estilo de programación asíncrona dirigida por eventos, tiene más sentido ver el anidamiento en términos de deep = > sucede más tarde que .

EDITAR: intente agregar algunas instrucciones de registro inmediatamente antes del final de cada función anidada; Esto ayudará a ilustrar que el orden en que se completan es desde el exterior hacia adentro.

Otros consejos

El mismo ejemplo, con salida de depuración agregada (ver más abajo para salida):

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

Salida:

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

Puedes probar

http://github.com/creationix/do

o enrolla el tuyo como lo hice yo. Por ahora, no importa perder el manejo de errores (solo ignóralo);)

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

Y un simple ejemplo usándolo

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

Tus cosas están bien. Hago llamadas recursivas en Express para seguir las redirecciones HTTP, pero lo que estás haciendo es " traversal " y no recursion

También eche un vistazo a 'step' ( http://github.com/creationix/step) o 'flow-js' en github. Esto le permite escribir flujos de devolución de llamada en un estilo más natural. Esto también dejará claro que no hay recursión.

Como con cualquier JavaScript, es posible hacer llamadas recursivas con Node.js. Si tiene problemas de profundidad de recursión (como señala NickFitz, no parece estar en peligro de hacerlo), a menudo puede volver a escribir su código para usar un temporizador de intervalos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top