2012-10-10 11 views
5

Vorrei porre questa domanda, perché non sono sicuro di avere la logica Node.js giustaNode.js & Redis; In attesa di un ciclo per terminare

Ho un set di ID che ho bisogno di interrogare usando redis 'get metodo. E dopo aver controllato un certo valore (diciamo che sto controllando se l'oggetto che ottengo con la "chiave" data ha un nome nullo), li aggiungo a una lista. Ecco il mio codice di esempio;

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

Ma come previsto a causa della natura asincrona del Nodo, senza controllare ogni singolo id nella lista esegue la "va qui ...". Il mio bisogno è di applicare il resto delle procedure dopo aver controllato ogni id (mappando in redis db e controllando il nome). Ma non so come farlo. Forse se posso associare il callback al ciclo for e assicurare che il resto delle mie funzioni inizi a funzionare dopo che il ciclo è terminato (so che è impossibile ma solo per dare un'idea)?

risposta

4

vorrei andare via lei suggerisce nella tua domanda e allegare un callback personalizzato alla funzione recupero:

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

Questo è probabilmente il metodo più efficiente; in alternativa si può fare affidamento su una vecchia scuola while ciclo fino a quando i dati sono pronti:

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

Usiamo process.nextTick invece di un vero e proprio while per assicurarsi che altre richieste non sono bloccate nel frattempo; a causa della natura a thread singolo di Javascript questo è il modo preferito. Lo sto lanciando per completezza, ma il precedente metodo è più efficiente e si adatta meglio con node.js, quindi provatelo a meno che non sia coinvolta una riscrittura importante.

Non vale nulla che entrambi i casi si basino su richiami asincroni, il che significa che qualsiasi codice al di fuori di esso può ancora essere eseguito prima che altri vengano eseguiti. Per esempio, utilizzando il nostro primo frammento:

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

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

console.log("hello world"); 

Quell'ultima console.log è quasi garantito per funzionare prima del nostro passato al callback getStudentsData viene licenziato. Soluzione? Progetta per questo, è come funziona node.js. Nel nostro caso sopra è facile, dovremmo chiamare console.log solo nel nostro callback passato a getStudentsData e non al di fuori di esso. Altri scenari richiedono soluzioni che si discostano un po 'di più dalla codifica procedurale tradizionale, ma una volta che ci si capisce da capo si scoprirà che essere guidati dagli eventi e non bloccanti è in realtà una caratteristica piuttosto potente.

+0

Primo esempio non sembra funzionare per me :(Vedere [questo] (https://i.gyazo.com/129b071f39bbd1a1c491638be634b00c.png), la chiamata Redis è asincrona Il. il log della console dei miei 'risultati' verrebbe mostrato per primo poiché è la stessa logica dell'esempio, hmmm –

1

Provare il modulo async per node.js. Quel modulo ha per ogni asincrono.

3

Prova modulo finish. Ho creato questo modulo per affrontare questo problema. È più facile da usare di Async e offre anche prestazioni migliori. Ecco un esempio:.

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

Dovrebbe pubblicare un campione del codice e il collegamento, in questo modo se il collegamento diventa morto il post non è inutile. – Matthew

+0

Grazie codice aggiunto – Chaoran

+1

WARN: 'finish' al momento si strozza se l'array you' forEach' è vuoto ed esce con" TypeError: non può leggere la proprietà 'kickoff' di indefinito. " Ci ho messo un po 'per tracciare che giù, spero che salvi qualcun altro un po 'di dolore![Vedi il problema su Github] (https://github.com/chaoran/node-finish/issues/2). – OJFord

Problemi correlati