Самый простой способ дождаться завершения некоторых асинхронных задач в Javascript?


Я хочу удалить некоторые коллекции mongodb, но это асинхронная задача. Код будет такой:

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

консоль отображает:

all dropped
dropped
dropped
dropped

что это самый простой способ, чтобы убедиться, что all dropped будет напечатан после того, как все коллекции были отброшены? Любая третья сторона может быть использована для упрощения кода.

8 105

8 ответов:

Я вижу, вы используете mongoose Итак, вы говорите о серверном JavaScript. В таком случае я советую смотреть на асинхронный модуль и использовать async.parallel(...). Вы найдете этот модуль очень полезным - он был разработан, чтобы решить проблему, с которой вы боретесь. Ваш код может выглядеть так

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

использовать обещания.

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

это отбрасывает каждую коллекцию, печатая " dropped "после каждого, а затем печатает" all dropped " по завершении. Если возникает ошибка, она отображается stderr.


предыдущий ответ (это предварительная поддержка узла для обещаний):

использовать Q обещания или птица обещания.

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

С птица:

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

способ сделать это-передать задачи обратного вызова, который обновляет общий счетчик. Когда общий счетчик достигает нуля, вы знаете, что все задачи завершены, поэтому вы можете продолжить свой обычный поток.

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

конечно, есть много способов, чтобы сделать этот код более универсальный и многоразовый и многие библиотеки асинхронного программирования там должна быть хотя бы одна функция, чтобы делать такие вещи.

расширяя @freakish ответ, async также предлагает каждый метод, который кажется особенно подходящим для вашего случая:

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

ИМХО, это делает код более эффективным и более четким. Я взял на себя смелость удалить console.log('dropped') - Если вы хотите его, используйте это вместо:

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

Я делаю это без внешних libaries:

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

все ответы довольно старые. С начала 2013 года Мангуст начал поддерживать обещания постепенно для всех запросов, так что это будет рекомендуемый способ структурирования нескольких асинхронных вызовов в требуемом порядке, я думаю.

Если вы используете Babel или такие транспилеры и используете async / await, вы можете сделать:

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

С deferred (еще одно обещание / отложенная реализация) вы можете сделать:

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