Pergunta

Em NodeJS, há praticamente nenhum bloqueio I / O operações. Isto significa que quase todos NodeJS código IO envolve muitos retornos de chamada. Isso se aplica a ler e escrever para / de bancos de dados, arquivos, processos, etc. Um exemplo típico disso é o seguinte:

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

Estou antecipando escrever código que fará muitos operações de IO, por isso espero estar escrevendo muitos callbacks. Estou muito confortável com o uso de chamadas de retorno, mas estou preocupado com toda a recursividade. Estou em perigo de ficar em muitos recursão e soprando através de um algum lugar da pilha? Se eu fazer milhares de gravações individuais a minha loja do valor-chave com milhares de chamadas de retorno, será o meu programa, eventualmente, falhar?

Am I mal-entendido ou subestimar o impacto? Se não, há uma maneira de contornar isso enquanto ainda estiver usando estilo de codificação callback NodeJS'?

Foi útil?

Solução

Nenhum do código que você mostrar é usando recursão. Quando você chama useFile chama posix.stat(), que retorna, e termina useFile como ele foi executado para conclusão. Em algum tempo depois, quando a chamada para posix.stat() completou dentro do sistema subjacente e os resultados estão disponíveis, a função de retorno de chamada que você adicionou para o que será executado. Que as chamadas posix.open(), e depois termina, uma vez que foi executado para conclusão. Uma vez que o arquivo foi aberto com sucesso, a função de retorno de chamada para que será executado, chamando posix.read(), e em seguida, encerrar, uma vez que, também, foi executado para conclusão. Finalmente, quando os resultados da leitura estão disponíveis, a função mais interna será executada.

O ponto importante é que cada função é executada até a conclusão, como as chamadas para as funções posix.*() são non-blocking: isto é, eles retornam imediatamente, tendo causado um pouco de magia para ser iniciado off no sistema subjacente. Assim, cada uma das suas funções termina, e mais tarde um evento fará com que a próxima função a executar; mas em nenhum momento há qualquer recursão.

A estrutura aninhada do código pode dar a impressão de que o interior coisas terá que terminar antes do exterior material pode chegar a seu próprio ponto final. Mas neste estilo de assíncrono evento-driven programação que faz mais sentido para ver o assentamento em termos de mais profundo => acontece-depois-que .

EDIT: Tente adicionar algumas declarações de registro imediatamente antes do final de cada função aninhada; isso vai ajudar a ilustrar que a ordem em que eles completa é de fora para dentro.

Outras dicas

O mesmo exemplo, com a saída de depuração adicional (veja abaixo para saída):

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

Você pode tentar

http://github.com/creationix/do

ou rolo seu próprio como eu fiz. Não importa faltando tratamento de erros por agora (apenas ignorar isso);)

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 um exemplo simples de usá-lo

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

Seu material é bom. Eu faço chamadas recursivas em Express para seguir redirecionamentos HTTP, mas o que você está fazendo é "transversal" e não recursão

Também dê uma olhada em 'passo' ( http://github.com/creationix/step) ou 'fluxo-js' no GitHub. Isso permite que você escrever callback flui em um estilo mais natural. Isso também vai deixar claro que não há nenhuma recursão acontecendo.

Como acontece com qualquer JavaScript, é possível fazer chamadas recursivas com Node.js. Se você se deparar com problemas de profundidade de recursão (como NickFitz aponta, você não parecem estar em perigo de isso), muitas vezes você pode reescrever seu código para usar um temporizador de intervalos vez.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top