2016-04-27 18 views
6

Sto utilizzando Node.js e Box SDK. La mia (! Non) il codice è simile al seguente:Node.js: come gestite i callback in un ciclo?

var connection = box.getConnection(req.user.login); 
connection.ready(function() { 
    connection.getFolderItems(0, null, function (err, result) { 
    if (err) { 
     opts.body = err; 
    } else { 
     opts.body = result; 
     var a = []; 
     for (var i=0; i < result.entries.length; i++) { 
     connection.getFileInfo(result.entries[i].id, function (err, fileInfo) { 
     if (err) { 
      opts.body = err; 
     } else { 
      a.push(fileInfo); 
     } 
     });} 
    } 

In termini "procedurali", questo è che io sto cercando di fare:

var connection= box.getConnection() 
var items = connection.getFolderItems() 
var itemList = new List 
foreach (item in items) { 
    connection.getFileInfo(item.id) 
    itemList.add(item) 
} 
display(itemList) 

mio problema è che connection.getFolderItems() e connection.getFileInfo() sono asincroni - il ciclo "for" termina prima che vengano restituiti tutti i risultati.

Q: Qual è il modo migliore in Node.js per 1) ottenere il risultato della prima chiamata asincrona, 2) scorrere l'elenco, effettuare più chiamate asincrone e 3) elaborare i risultati quando tutto è "completato" ".

Q: Sono promises una buona scelta qui?

Q: È done()/next() un'opzione?

D: Esiste un "linguaggio standard" in Node.js per questo tipo di scenario?

risposta

8

Le promesse sono una grande idea, ma si consiglia di dare un'occhiata al modulo di asincrona, in particolare i gestori di raccolta. Ti permette di eseguire chiamate asincrone contro un elenco di "cose" e ti dà un posto dove eseguire un metodo quando tutte le chiamate asincrone sono fatte. Non so se questo è meglio di rispetto alle promesse, ma le opzioni sono sempre belle.

// Should give you a rough idea 
async.each(items, function (item, callback) { 
    connection.getFileInfo(result, callback); 
}, function (err) { 
    console.log('All done'); 
}); 

https://github.com/caolan/async#each

+0

Tutte le risposte che ho ricevuto sono stati grandi - grazie! Ho finito con l'uso asincrono, come suggerito da te e JasonWihardja. Ecco un buon tutorial: http://justinklemm.com/node-js-async-tutorial/ – paulsm4

+0

Continuo a pensare che le promesse siano in definitiva il modo più idiomatico di farlo, ma sono contento che tu l'abbia trovato utile. – skarface

5

D: Qual è il modo migliore in Node.js a 1) ottenere il risultato della prima chiamata asincrona, 2) scorrere l'elenco, rendendo più chiamate asincrone, e 3) elaborare i risultati quando Tutto è fatto".

Ci sono diversi approcci. Codifica delle mani, promesse, libreria asincrona. "Il meglio" è negli occhi di chi guarda, quindi non è proprio il caso di dirlo qui. Io uso Promises per tutta la mia codifica asincrona. Sono stati ufficialmente standardizzati in ES6 e ci sono buone e robuste implementazioni (mi piace Bluebird per le caratteristiche extra che ha oltre lo standard che semplificano le complesse attività asincrone e per la sua funzione promisifyAll() che ti offre un'interfaccia promettente su qualsiasi operazione asincrona standard che usi la convenzione chiamata callback asincrono).

Mi sconsiglio di codificare a mano complicate operazioni asincrone perché la gestione degli errori è molto difficile e le eccezioni possono essere silenziosamente consumate all'interno di richiami asincroni che portano alla gestione degli errori persi e al debugging difficile. La libreria Async è probabilmente il miglior modo non promettente di fare le cose in quanto fornisce alcune funzionalità di infrastruttura e sincronizzazione attorno a callback asincroni.

Personalmente preferisco le promesse. Penso che vedremo più API asincrone che si standardizzano sulla restituzione di una promessa con il passare del tempo, quindi penso che sia una scelta migliore per un modo lungimirante di apprendere e programmare.

Q: Le promesse sono una buona scelta qui?

Sì, promesse permettono di eseguire una serie di operazioni asincrone e quindi utilizzare qualcosa come Promise.all() sapere quando sono tutti fatti. Raccoglierà anche tutti i risultati di tutte le operazioni asincrone per te.

Q: È stata fatta()/successiva() un'opzione?

io non sono esattamente sicuro di quello che stai chiedendo circa qui, ma è possibile manualmente le operazioni asincrone codice a uno in parallelo e sapere quando sono fatto o per eseguirli in sequenza e sapere quando sono fatto. Le promesse fanno molto di più di questo lavoro per te, ma puoi farlo manualmente senza di loro.

D: Esiste un "linguaggio standard" in Node.js per questo tipo di scenario?

Se si utilizzano le promesse, ci sarebbe un modo comune per farlo. Se non si utilizzano le promesse, probabilmente non esiste un "linguaggio standard" in quanto vi sono molti modi diversi per codificarlo autonomamente.

Attuazione Promessa

Ecco un esempio utilizzando la libreria Bluebird Promessa in node.js:

var Promise = require('bluebird'); 
var connection = Promise.promisifyAll(box.getConnection(req.user.login)); 
connection.ready(function() { 
    connection.getFolderItemsAsync(0, null).then(function(result) { 
     return Promise.map(result.entries, function(item) { 
      return connection.getFileInfoAsync(item.id); 
     }) 
    }).then(function(results) { 
     // array of results here 
    }, function(err) { 
     // error here 
    }); 
}); 

Ecco come funziona:

  1. Promisify l'oggetto di connessione in modo che tutti i suoi metodi hanno una versione che restituisce una promessa (basta aggiungere "Async" alla fine del metodo per chiamare questo ballo di fine anno versione isificata).

  2. chiamata getFolderItemsAsync() e la sua promessa si risolverà con la result.entries serie

  3. Esegui una mappa di tale matrice, in esecuzione tutte le operazioni in parallelo e restituendo una promessa che si risolve con una serie di risultati ordinati quando tutte le operazioni sono fatti.

  4. Il risultato effettivo per ogni voce viene raggiunto con connection.getFileInfoAsync().

  5. Creare un gestore di risoluzione e un gestore di rifiuti. Se qualche errore si verifica in qualsiasi parte del processo, si propagherà al gestore di rifiuti. Se tutte le operazioni hanno esito positivo, l'ultimo gestore di risoluzione verrà chiamato con una serie ordinata di risultati.

La versione sopra riportata si interrompe se si verifica un errore e non si ottengono risultati diversi dal codice di errore. Se si desidera continuare con un risultato null se c'è un errore, allora si può usare qualcosa di simile:

var Promise = require('bluebird'); 
var connection = Promise.promisifyAll(box.getConnection(req.user.login)); 
connection.ready(function() { 
    connection.getFolderItemsAsync(0, null).then(function(result) { 
     return Promise.map(result.entries, function(item) { 
      return connection.getFileInfoAsync(item.id).catch(function(err){ 
       // post the results as null and continue with the other results 
       return null; 
      }); 
     }) 
    }).then(function(results) { 
     // array of results here (some might be null if they had an error) 
    }, function(err) { 
     // error here 
    }); 
}); 

manualmente Coded Versione

Ecco una versione in codice manualmente. La chiave per questo sta rilevando quando il tuo ciclo asincrono è fatto confrontando if (results.length === result.entries.length). Nota: questo ha una gestione degli errori incompleta che è una delle difficoltà di codifica manuale e non utilizza una struttura asincrona come le promesse.

var connection = box.getConnection(req.user.login); 
connection.ready(function() { 
    connection.getFolderItems(0, null, function (err, result) { 
     if (err) { 
      // do error handling here 
      opts.body = err; 
     } else { 
      var results = []; 
      for (var i = 0; i < result.entries.length; i++) { 
       connection.getFileInfo(result.entries[i].id, function (err, fileInfo) { 
        if (err) { 
         // do error handling here 
         opts.body = err; 
         results.push(null); 
        } else { 
         results.push(fileInfo); 
        } 
        // if done with all requests 
        if (results.length === result.entries.length) { 
         // done with everything, results are in results 
         // process final results here 
        } 
       }); 
      } 
     } 
    }); 
}); 
4

D: Qual è il modo migliore in Node.js a 1) ottenere il risultato della prima chiamata asincrona, 2) scorrere l'elenco, rendendo più chiamate asincrone, e 3) Processo i risultati quando tutto è "fatto".

È possibile utilizzare la libreria async o promettere le chiamate di funzione e utilizzare invece Promessa. Entrambi sono facili da usare.

Q: Le promesse sono una buona scelta qui?

Sì. Ma richiede di promettere il tuo metodo prima di usarlo.

Q: È stata fatta()/successiva() un'opzione?

Da quello che ho capito, è un concetto completamente diverso. Il done qui si riferisce a una funzione di callback che è possibile chiamare dopo il completamento del metodo. E next di solito viene utilizzato in espresso per passare una richiesta al prossimo percorso, che a mio avviso non è pertinente alla domanda che stai chiedendo.

D: Esiste un "linguaggio standard" in Node.js per questo tipo di scenario?

E 'di solito denominato semplicemente "asincrona" o "non bloccante" chiama