web-dev-qa-db-fra.com

Le moyen le plus simple d’attendre certaines tâches asynchrones en Javascript?

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.

110
Freewind

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

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.


Réponse précédente (cette option est antérieure à la prise en charge native de Node pour Promises):

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);
125
Nate

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.

21
hugomg

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');
});
7
Erwin Wessels

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');
        }
    });                
});
5
user435943

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.

4
Capaj

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");
}
1
ganaraj

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);
0
Mariusz Nowak