De eenvoudigste manier om een ​​aantal asynchrone taken af ​​te wachten, in Javascript?

Ik wil wat mongodb-verzamelingen verwijderen, maar dat is een asynchrone taak. De code is:

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');

De console geeft het volgende weer:

all dropped
dropped
dropped
dropped

Wat is de eenvoudigste manier om ervoor te zorgen dat all droppedwordt afgedrukt nadat alle verzamelingen zijn verwijderd? Elke derde partij kan worden gebruikt om de code te vereenvoudigen.


Antwoord 1, autoriteit 100%

Gebruik Beloften.

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

Hierdoor wordt elke verzameling verwijderd, wordt na elke verzameling ‘verwijderd’ afgedrukt en wordt ‘alle verwijderd’ afgedrukt wanneer deze is voltooid. Als er een fout optreedt, wordt deze weergegeven aan stderr.


Vorige antwoord (dit dateert van vóór de native ondersteuning van Node voor Promises):

Gebruik Qbeloften of Bluebirdbelooft.

Met 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);

Met 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);

Antwoord 2, autoriteit 71%

Ik zie dat je mongoosegebruikt, dus je hebt het over server-side JavaScript. In dat geval raad ik aan om naar async-modulete kijken en async.parallel(...). U zult merken dat deze module erg nuttig is – hij is ontwikkeld om het probleem op te lossen waarmee u worstelt. Uw code kan er zo uitzien

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

Antwoord 3, autoriteit 16%

De manier om dit te doen is om de taken een callback door te geven die een gedeelde teller bijwerkt. Wanneer de gedeelde teller op nul staat, weet je dat alle taken zijn voltooid, zodat je door kunt gaan met je normale stroom.

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

Natuurlijk zijn er veel manieren om dit soort code generiek of herbruikbaar te maken en elk van de veel asynchrone programmeerbibliothekendie er zijn, zouden ten minste één functie moeten hebben om dit soort dingen te doen.


Antwoord 4, autoriteit 6%

Voortbouwend op het @freakish-antwoord, biedt async ook elke methode, die bijzonder geschikt lijkt voor uw geval:

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, dit maakt de code zowel efficiënter als leesbaarder. Ik ben zo vrij geweest om de console.log('dropped')te verwijderen – als je dat wilt, gebruik dan dit:

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

Antwoord 5, autoriteit 4%

Ik doe dit zonder externe bibliotheken:

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

Antwoord 6, autoriteit 3%

Alle antwoorden zijn vrij oud. Sinds begin 2013 is Mongoose begonnen met het geleidelijk ondersteunen van beloftenvoor alle vragen, zodat zou de aanbevolen manier zijn om in de toekomst verschillende asynchrone oproepen in de vereiste volgorde te structureren, denk ik.


Antwoord 7

Met deferred(nog een belofte/uitgestelde implementatie) kunt u het volgende doen:

// 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);

Antwoord 8

Als u Babel of dergelijke transpilers gebruikt en async/wait gebruikt, kunt u het volgende doen:

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

Other episodes