2013-08-02 25 views
32

Sto provando a creare più query MongoDB prima di eseguire il rendering di un modello Jade, ma non riesco a capire come attendere fino a quando tutte le query Mongo sono state completate prima di rendere il modello .Node.js - attendere più chiamate asincrone

exports.init = function(req, res){ 


     var NYLakes = {}; 
     var NJLakes = {}; 
     var filterNY = {"State" : "NY"}; 
     db.collection('lakes').find(filterNY).toArray(function(err, result) { 
      if (err) throw err; 
      NYLakes = result; 
     }); 

     var filterNJ = {"State" : "NJ"}; 
     db.collection('lakes').find(filterNJ).toArray(function(err, result) { 
      if (err) throw err; 
      NJLakes = result; 
     }); 

     res.render('explore/index', 
      { 
       NYlakes: NYLakes, 
       NJlakes: NJLakes 
      } 
     ); 

    }; 

risposta

17

Supponendo che si desidera eseguire le due operazioni in parallelo piuttosto che aspettare per uno a finire prima di iniziare la successiva, è necessario tenere traccia di quante operazioni hanno completato in ogni callback.

In node.js prime javascript, un modo per fare questo sarebbe questo:

exports.init = function(req, res){ 
    var NYLakes = null; 
    var NJLakes = null; 
    var filterNY = {"State" : "NY"}; 

    db.collection('lakes').find(filterNY).toArray(function(err, result) { 
     if (err) throw err; 
     NYLakes = result; 
     complete(); 
    }); 

    var filterNJ = {"State" : "NJ"}; 
    db.collection('lakes').find(filterNJ).toArray(function(err, result) { 
     if (err) throw err; 
     NJLakes = result; 
     complete(); 
    }); 

    function complete() { 
     if (NYLakes !== null && NJLakes !== null) { 
      res.render('explore/index', { 
       NYlakes: NYLakes, 
       NJlakes: NJLakes 
      }); 
     } 
    } 

}; 

In sostanza quello che sta succedendo qui è che si controlla al termine di ogni operazione, se tutti hanno finito, e al quel punto finisci l'operazione.

Se stai facendo un sacco di queste cose, dai un'occhiata alla libreria async come esempio di uno strumento per rendere più facile la gestione di questo tipo di cose.

+0

Molto pulito. Bello. –

+0

Questo avrebbe una condizione di gara. Se entrambe le operazioni asincrone vengono eseguite fino a destra prima che chiamino complete(), allora potrebbero chiamarle con la condizione necessaria per eseguire il corpo dell'istruzione if in complete(). Improbabile, ma possibile. –

+0

@ ghert85 In un ambiente con multithreading avresti ragione, ma node.js è single threaded. Quindi il codice non può essere interrotto fino a quando non ritorna al ciclo degli eventi e questa condizione di competizione non può verificarsi. –

5

È possibile utilizzare async modulo:

var states = [{"State" : "NY"},{"State" : "NJ"}]; 

var findLakes = function(state,callback){ 
    db.collection('lakes').find(state).toArray(callback); 
} 

async.map(states, findLakes , function(err, results){ 
    // do something with array of results 
}); 
52

Sono un grande fan di sottolineatura/lodash, così io di solito usare _.after, che crea una funzione che viene eseguita solo dopo essere stato chiamato un certo numero di volte.

var finished = _.after(2, doRender); 

asyncMethod1(data, function(err){ 
    //... 
    finished(); 
}); 

asyncMethod2(data, function(err){ 
    //... 
    finished(); 
}) 

function doRender(){ 
    res.render(); // etc 
} 

Dal javascript issa la definizione di funzioni definite con la sintassi function funcName(), il codice di legge naturale: top-to-bottom.

+0

Eccellente, grazie :) – iConnor

1

Wait.for https://github.com/luciotato/waitfor

utilizzando Wait.for:

exports.init = function(req, res){ 

    var NYLakes = {}; 
    var NJLakes = {}; 

    var coll = db.collection('lakes'); 

    var filterNY = {"State" : "NY"}; 
    var a = wait.forMethod(coll,'find',filterNY); 
    NYLakes = wait.forMethod(a,'toArray'); 

    var filterNJ = {"State" : "NJ"}; 
    var b = wait.forMethod(coll,'find',filterNJ); 
    NJLakes = wait.forMethod(b,'toArray'); 

    res.render('explore/index', 
     { 
      NYlakes: NYLakes, 
      NJlakes: NJLakes 
     } 
    ); 

}; 

Richiesta in parallelo utilizzando wait.for mappa parallelo:

exports.init = function(req, res){ 

    var coll = db.collection('lakes'); 

    //execute in parallel, wait for results 
    var result = wait.parallel.map(
        [{coll:coll,filter:{"State" : "NY"}} 
        , {coll:coll,filter:{"State" : "NJ"}}] 
        , getData); 

    res.render('explore/index', 
     { 
      NYlakes: result[0], 
      NJlakes: result[1] 
     } 
    ); 

}; 

//map function 
function getData(item,callback){ 
try{ 
    var a = wait.forMethod(item.coll,'find',item.filter); 
    var b = wait.forMethod(a,'toArray'); 
    callback (null, b); 
} catch(err){ 
    callback(err); 
} 

io non sono a conoscenza con mongo, quindi potresti dover aggiustare le chiamate.

Problemi correlati