用Javascript等待异步任务完成的最简单方法?

用Javascript等待异步任务完成的最简单方法?,javascript,node.js,asynchronous,mongoose,synchronous,Javascript,Node.js,Asynchronous,Mongoose,Synchronous,我想删除一些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) { conso

我想删除一些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
最简单的方法是什么,以确保在删除所有集合后打印所有删除的集合?任何第三方都可以用来简化代码。

我看到您使用的是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 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);
当然,有很多方法可以使这类代码更通用或可重用,其中任何一种都应该至少有一个函数来完成这类工作。

使用

这会删除每个集合,在每个集合之后打印“已删除”,然后在完成后打印“全部已删除”。如果发生错误,它将显示给stderr

前面的回答预先说明了此节点对承诺的本机支持: 使用承诺或承诺

问:

蓝知更鸟:

通过延迟的另一个承诺/延迟的实施,您可以做到:

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

async扩展了@freakish answer,还提供了一个each方法,它似乎特别适合您的情况:

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

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

我这样做时没有外部库:

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

如果您使用Babel或类似的Transpiler并使用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");
}

用这个。。。forEach方法是异步的。因此,如果对象列表比这里详述的3个长,那么当async.parallelcalls、functionerr、result is evaluated调用尚未包含原始列表中的所有函数时,是否会出现这种情况?@MartinBeeby forEach是同步的。看看这里:底部是forEach的实现。并不是所有回调函数都是异步的。作为记录,async也可以在浏览器中使用。@如果所有回调函数都是异步的,那么问题是forEach并没有被传递回调函数,而只是一个常规函数,这是Mozilla使用术语的错误。在函数式编程语言中,永远不会调用传递的函数callback@ghert85不,术语没有问题。回调就是任何作为参数传递给其他代码的可执行代码,并期望在某个时刻执行。这是标准定义。它可以被同步或异步调用。看看这个:这可能不是最容易实现的,但是我真的很喜欢看到一个不需要外部模块的答案。非常感谢。承诺是前进的道路。是另一个promise库,如果这是在性能关键的代码中,它将很好地工作。这应该是一个替代品。只需使用require'bluebird'。我添加了一个bluebird示例。这有点不同,因为使用Bluebird的最佳方式是使用promisifyAll功能。知道promisifyAll是如何工作的吗?我读过文档,但我不明白它是如何处理没有参数的函数的,比如函数abcdata{,因为它不像函数abcerr,回调{…基本上我不认为所有函数都将error作为第一个参数,将callback作为第二个参数param@MuhammadUmer很多细节,MongoDB驱动程序也支持承诺已经有一段时间了。你能更新你的示例来利用它吗?.mapfunctionname{return conn.collectionname.drop}您不能传递回调以删除并期望返回承诺。您能修复此示例并删除onDrop吗?
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');
});
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');
        }
    });                
});
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");
}