2015-09-03 15 views
11

Sto tentando di utilizzare il metodo lodash forEach con una funzione nidificata che chiama un database mongo.Lodash: _.forOgni con funzione

var jobs = []; 
_.forEach(ids, function(id) { 
    JobRequest.findByJobId(id, function(err, result) { 
     if(err) callback(err); 
     jobs.push(result); 
    }); 
}); 

callback(null, jobs); 

sto avendo problemi a causa della forEach e callback verrà eseguito attraverso prima la funzione interna viene mai chiamato. Come posso risolvere questo?

Desidero richiamare il callback dopo che per ogni funzione interna e completata.

+0

Si dovrebbe usare 'async.map'. – SLaks

risposta

8

JobRequest.findByJobId è un'operazione asincrona. Non è possibile bloccare operazioni asincrone in JavaScript, quindi è necessario sincronizzarlo manualmente contando. Esempio (gestione degli errori omesso per brevità):

var results = []; 
var pendingJobCount = ids.length; 

_.forEach(ids, function(id) { 
    JobRequest.findByJobId(id, function(err, result) { 
     results.push(result); 
     if (--pendingJobCount === 0) callback(null, results); 
    }); 
}); 

Ci sono, naturalmente, costrutti wrapper per fare cose come questa, ma preferisco spiegare come funziona realmente. Controlla dfsq's answer per ulteriori dettagli su uno di questi wrapper, chiamato promesse.

Si noti inoltre che le operazioni asincrone possono essere completate. L'ordine nell'array results non corrisponde necessariamente all'ordine della matrice ids. Se avete bisogno di informazioni che collegato, è necessario tenere traccia da soli, ad esempio raccogliendo i risultati in una mappa invece di un array:

var results = {}; 
var pendingJobCount = ids.length; 

_.forEach(ids, function(id) { 
    JobRequest.findByJobId(id, function(err, result) { 
     results[id] = result; 
     if (--pendingJobCount === 0) callback(null, results); 
    }); 
}); 

questo esempio si presuppone che non ci sono duplicati nella vostra ids array. I risultati per le chiavi duplicate verranno ignorati.

La gestione degli errori potrebbe funzionare in modo simile, inserendo informazioni aggiuntive nel risultato. Un altro esempio:

results.push({id: id, error: null, value: result}); 
11

Un altro approccio è quello di avvolgere tutto in promesse, in questo caso i risultati del lavoro saranno spinti in una matrice in corretto ordine:

var promises = ids.map(function(id) { 
    return new Promise(function(resolve, reject) { 
     JobRequest.findByJobId(id, function (err, result) { 
      if (err) reject(err); 
      resolve(result); 
     }); 
    }); 
}); 

Promise.all(promises).then(function(jobs) { 
    callback(null, jobs); 
}, callback); 

// or shorter: Promise.all(promises).then(callback.bind(null, null), callback); 

Nota, che è anche necessario per gestire il potenziale situazione in cui la richiesta JobRequest.findByJobId non riesce, con promesse è molto semplice: basta passare callback come callback di errore a Promise.all.

+1

Supponendo un moderno motore JavaScript (o un buon polyfill), questo è sicuramente uno dei modi più leggibili per farlo! – jwueller