2012-10-10 39 views
5

Me gustaría formular esta pregunta, porque no estoy seguro si obtuve la lógica Node.js correctaNode.js y Redis; Esperando que termine un ciclo

Tengo un conjunto de id que necesito consultar usando redis 'get método. Y después de verificar un cierto valor (digamos que estoy comprobando si el objeto que recibo con la "clave" dada tiene un nombre nulo), los agrego a una lista. Aquí está mi código de ejemplo;

var finalList = []; 
var list = []; 
redisClient.smembers("student_list", function(err,result){ 
      list = result; //id's of students 
      console.log(result); 

      var possibleStudents = []; 


      for(var i = 0; i < list.length; i++){ 


       redisClient.get(list[i], function(err, result){ 
        if(err) 
         console.log("Error: "+err); 
        else{ 
         tempObject = JSON.parse(result); 
         if(tempObject.name != null){ 
          finalList.push(tempObject); 
         } 
        } 
       });  
      } 

    }); 
    console.log("Goes here after checking every single object"); 

Pero como se esperaba debido a la naturaleza asíncrona de nodo, sin comprobar cada id en la lista se ejecuta el "va aquí ...". Mi necesidad es aplicar el resto de los procedimientos después de que se compruebe cada identificación (mapeo en redis db y verificar el nombre). Pero no sé cómo hacerlo. Tal vez si puedo adjuntar una devolución de llamada al ciclo for y asegurarme de que el resto de mis funciones comiencen a ejecutarse después de que termine el ciclo (sé que es imposible, pero solo para dar una idea).

Respuesta

4

Me gustaría ir a la ruta que usted sugiere en su pregunta y adjunte una devolución de llamada personalizado a la función de ir a buscar:

function getStudentsData(callback) { 
    var setList = []; 
    var dataList = []; 

    redisClient.smembers("student_setList", function(err,result) { 
     setList = result; //id's of students 

     for(var i = 0; i < setList.length; i++) { 
      redisClient.get(setList[i], function(err, result) { 
       if(err) { 
        console.log("Error: "+err); 
       } else { 
        tempObject = JSON.parse(result); 
        if(tempObject.name != null) { 
         dataList.push(tempObject); 
        } 
       } 
      });  
     } 

     if(dataList.length == setList.length) { 
      if(typeof callback == "function") { 
       callback(dataList); 
      } 
      console.log("getStudentsData: done"); 
     } else { 
      console.log("getStudentsData: length mistmach"); 
     } 

    }); 
} 

getStudentsData(function(dataList) { 
    console.log("Goes here after checking every single object"); 
    console.log(dataList.length); 
    //More code here 
}); 

Eso es probablemente el método más eficiente; otra posibilidad es confiar en un viejo lazo de la escuela while hasta que los datos están listos:

var finalList = []; 
var list = [0]; 

redisClient.smembers("student_list", function(err,result) { 
    list = result; //id's of students 
    var possibleStudents = []; 

    for(var i = 0; i < list.length; i++) { 
     redisClient.get(list[i], function(err, result) { 
      if(err) { 
       console.log("Error: "+err); 
      } else { 
       tempObject = JSON.parse(result); 
       if(tempObject.name != null) { 
        finalList.push(tempObject); 
       } 
      } 
     });  
    } 
}); 


process.nextTick(function() { 
    if(finalList.length == list.length) { 
     //Done 
     console.log("Goes here after checking every single object"); 
     console.log(dataList.length); 
     //More code here 
    } else { 
     //Not done, keep looping 
     process.nextTick(arguments.callee); 
    } 
}); 

Utilizamos process.nextTick en lugar de un verdadero while para asegurarse de que otras peticiones no están bloqueados en el ínterin; debido a la naturaleza de un solo subproceso de Javascript, esta es la forma preferida. Lo digo por completo, pero el método anterior es más eficiente y se ajusta mejor a node.js, por lo tanto, adelante, a menos que se trate de una reescritura importante.

No vale la pena que ambos casos se basen en devoluciones de llamada asíncronas, lo que significa que cualquier código que exista aún puede ejecutarse antes de que se completen otros. Por ejemplo, usando nuestro primer fragmento:

function getStudentsData(callback) { 
    //[...] 
} 

getStudentsData(function(dataList) { 
    //[...] 
}); 

console.log("hello world"); 

Esa última console.log es casi seguro que correr antes de nuestra devolución de llamada se pasa a getStudentsData es despedido. ¿Solución alternativa? Diseñe para eso, así es como funciona node.js. En nuestro caso anterior es fácil, simplemente llamaremos a console.log solo en nuestra devolución de llamada pasada a getStudentsData y no fuera de esta. Otros escenarios requieren soluciones que se aparten un poco más de la codificación de procedimientos tradicional, pero una vez que lo conozca, encontrará que ser impulsado por eventos y no bloquear es en realidad una característica bastante poderosa.

+0

Ver [esto] (https://i.gyazo.com/129b071f39bbd1a1c491638be634b00c.png), la llamada Redis es asíncrona El. El registro de la consola de mis 'resultados' se mostraría primero, ya que es exactamente la misma lógica que en su ejemplo, hmmm. –

1

Pruebe async módulo para node.js. Ese módulo tiene asincrónico para cada uno.

3

Pruebe el módulo finish. Creé este módulo para lidiar con este problema. Es más fácil de usar que Async y también ofrece un mejor rendimiento. He aquí un ejemplo:. Primer ejemplo no parece funcionar para mí :(

var finish = require("finish"); 
finish(function(async) { 
    // Any asynchronous calls within this function will be captured 
    // Just wrap each asynchronous call with function 'async' 
    ['file1', 'file2', 'file3'].forEach(function(file) { 
    async(function(done) { 
     // Your async function should use 'done' as callback, or call 'done' in its callback 
     fs.readFile(file, done); 
    }); 
    }); 
}, function(err, results) { 
    // fired after all asynchronous calls finish or as soon as an error occurs 
    console.log(results[0]);console.log(results[1]);console.log(results[2]); 
}); 
+2

Debe publicar muestras del código y del enlace. De esta forma, si el enlace se desactiva, la publicación no es inútil. – Matthew

+0

Gracias. Ejemplo de código agregado – Chaoran

+1

WARN: 'finish' actualmente se bloquea si la matriz que' forEach' está vacía, y sale con "TypeError: no se puede leer la propiedad 'kickoff' de indefinido". Me tomó un tiempo seguir esa pista abajo, ¡espero que le salve algo a alguien![Ver el problema en Github] (https://github.com/chaoran/node-finish/issues/2). – OJFord

Cuestiones relacionadas