node.js - 在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个解决方案
125 votes

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

这会丢弃每个集合,在每个集合之后打印“丢弃”,然后在完成时打印“全部丢弃”。 如果发生错误,则显示为stderr


以前的答案(这是Node对Promises的本机支持之前的事先):

使用Q promises或Bluebird承诺。

有问:

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

使用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);
Nate answered 2019-07-09T16:59:00Z
89 votes

我看到你正在使用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);
});
freakish answered 2019-07-09T16:57:49Z
21 votes

执行此操作的方法是将任务传递给更新共享计数器的回调。 当共享计数器达到零时,您知道所有任务都已完成,因此您可以继续正常流程。

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

当然,有很多方法可以使这种代码更通用或可重用,并且那里的任何异步编程库都应该至少有一个函数来完成这种事情。

hugomg answered 2019-07-09T16:59:40Z
7 votes

扩展@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');
});
Erwin Wessels answered 2019-07-09T17:00:14Z
5 votes

我这样做没有外部库:

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');
        }
    });                
});
user435943 answered 2019-07-09T17:00:44Z
4 votes

所有的答案都很古老。 自2013年初以来,Mongoose开始逐步支持所有查询的承诺,因此这将是推荐的方式,按照所需的顺序构建几个异步调用。

Capaj answered 2019-07-09T17:01:10Z
1 votes

如果您使用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");
}
ganaraj answered 2019-07-09T17:01:41Z
0 votes

使用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);
Mariusz Nowak answered 2019-07-09T17:02:11Z
translate from https://stackoverflow.com:/questions/10551499/simplest-way-to-wait-some-asynchronous-tasks-complete-in-javascript