Je veux supprimer certaines collections mongodb, mais c'est une tâche asynchrone. Le code sera:
var mongoose = require('mongoose');
mongoose.connect('mongo://localhost/xxx');
var conn = mongoose.connection;
['aaa','bbb','ccc'].forEach(function(name){
conn.collection(name).drop(function(err) {
console.log('dropped');
});
});
console.log('all dropped');
La console affiche:
all dropped
dropped
dropped
dropped
Quel est le moyen le plus simple de s'assurer que all dropped
sera imprimé après la suppression de toutes les collections? Toute tierce partie peut être utilisée pour simplifier le code.
Je vois que vous utilisez mongoose
donc vous parlez de JavaScript côté serveur. Dans ce cas, je vous conseille de regarder module asynchrone et d'utiliser async.parallel(...)
. Vous trouverez ce module vraiment utile - il a été développé pour résoudre le problème que vous rencontrez. Votre code peut ressembler à ceci
var async = require('async');
var calls = [];
['aaa','bbb','ccc'].forEach(function(name){
calls.Push(function(callback) {
conn.collection(name).drop(function(err) {
if (err)
return callback(err);
console.log('dropped');
callback(null, name);
});
}
)});
async.parallel(calls, function(err, result) {
/* this code will run after all calls finished the job or
when any of the calls passes an error */
if (err)
return console.log(err);
console.log(result);
});
Utilisez Promises .
var mongoose = require('mongoose');
mongoose.connect('your MongoDB connection string');
var conn = mongoose.connection;
var promises = ['aaa', 'bbb', 'ccc'].map(function(name) {
return new Promise(function(resolve, reject) {
var collection = conn.collection(name);
collection.drop(function(err) {
if (err) { return reject(err); }
console.log('dropped ' + name);
resolve();
});
});
});
Promise.all(promises)
.then(function() { console.log('all dropped)'); })
.catch(console.error);
Cela supprime chaque collection, en imprimant "abandonné" après chaque copie, puis en imprimant "tout supprimé" une fois l'opération terminée. Si une erreur survient, le message stderr
s'affiche.
Utilisez Q promesses ou Bluebird promesses.
Avec Q:
var Q = require('q');
var mongoose = require('mongoose');
mongoose.connect('your MongoDB connection string');
var conn = mongoose.connection;
var promises = ['aaa','bbb','ccc'].map(function(name){
var collection = conn.collection(name);
return Q.ninvoke(collection, 'drop')
.then(function() { console.log('dropped ' + name); });
});
Q.all(promises)
.then(function() { console.log('all dropped'); })
.fail(console.error);
Avec Bluebird :
var Promise = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));
mongoose.connect('your MongoDB connection string');
var conn = mongoose.connection;
var promises = ['aaa', 'bbb', 'ccc'].map(function(name) {
return conn.collection(name).dropAsync().then(function() {
console.log('dropped ' + name);
});
});
Promise.all(promises)
.then(function() { console.log('all dropped'); })
.error(console.error);
La façon de le faire consiste à transmettre aux tâches un rappel qui met à jour un compteur partagé. Lorsque le compteur partagé atteint zéro, vous savez que toutes les tâches sont terminées, ce qui vous permet de continuer avec votre flux normal.
var ntasks_left_to_go = 4;
var callback = function(){
ntasks_left_to_go -= 1;
if(ntasks_left_to_go <= 0){
console.log('All tasks have completed. Do your stuff');
}
}
task1(callback);
task2(callback);
task3(callback);
task4(callback);
Bien sûr, il y a beaucoup de façons de rendre ce genre de code plus générique ou réutilisable et n'importe lequel des beaucoup de bibliothèques de programmation async devrait avoir au moins une fonction pour faire ce genre de chose.
En développant la réponse @freakish, async vous propose également une méthode différente, ce qui semble particulièrement adapté à votre cas:
var async = require('async');
async.each(['aaa','bbb','ccc'], function(name, callback) {
conn.collection(name).drop( callback );
}, function(err) {
if( err ) { return console.log(err); }
console.log('all dropped');
});
IMHO, cela rend le code à la fois plus efficace et plus lisible. J'ai pris la liberté de supprimer la console.log('dropped')
- si vous le souhaitez, utilisez ceci à la place:
var async = require('async');
async.each(['aaa','bbb','ccc'], function(name, callback) {
// if you really want the console.log( 'dropped' ),
// replace the 'callback' here with an anonymous function
conn.collection(name).drop( function(err) {
if( err ) { return callback(err); }
console.log('dropped');
callback()
});
}, function(err) {
if( err ) { return console.log(err); }
console.log('all dropped');
});
Je le fais sans bibliothèques externes:
var yourArray = ['aaa','bbb','ccc'];
var counter = [];
yourArray.forEach(function(name){
conn.collection(name).drop(function(err) {
counter.Push(true);
console.log('dropped');
if(counter.length === yourArray.length){
console.log('all dropped');
}
});
});
Toutes les réponses sont assez anciennes. Depuis le début de 2013, Mongoose a commencé à prendre en charge promesses progressivement pour toutes les requêtes. Ce serait donc la méthode recommandée pour structurer plusieurs appels asynchrones dans l'ordre requis, je suppose.
Si vous utilisez Babel ou de tels transpilers et utilisez async/wait, vous pouvez faire:
function onDrop() {
console.log("dropped");
}
async function dropAll( collections ) {
const drops = collections.map(col => conn.collection(col).drop(onDrop) );
await drops;
console.log("all dropped");
}
Avec deferred
(autre promesse/implémentation différée), vous pouvez faire:
// Setup 'pdrop', promise version of 'drop' method
var deferred = require('deferred');
mongoose.Collection.prototype.pdrop =
deferred.promisify(mongoose.Collection.prototype.drop);
// Drop collections:
deferred.map(['aaa','bbb','ccc'], function(name){
return conn.collection(name).pdrop()(function () {
console.log("dropped");
});
}).end(function () {
console.log("all dropped");
}, null);