How to handle multiple async requests within a Grunt custom task?

I am working on a grunt task that installs the latest wordpress plugins from the wordpress svn repo, this is done via a command line task.

Ideally I would like this done synchronously so that I can see output as each plugin installs (via svn co) .. but it seems like exec simply executes and doesn't wait, so using var done = this.async() and done(error) works well with a single async action, but not multiple like in this case ... what am I missing?

grunt.registerTask('install-plugin', 'Get latest versions of required plugins', function(p){
    var exec = require('child_process').exec;
    var plugins = p ? [p] : grunt.config.get('wp_plugins');
    var pattern = 'svn co http://plugins.svn.wordpress.org/%s/tags/$(curl -s http://plugins.svn.wordpress.org/%s/trunk/readme.txt | grep "Stable tag:" | sed -E "s/[Ss]table tag: *//")/ plugins/%s'
    var done = this.async();
    plugins.forEach(function(plugin) {
        // using split/join instead of util.format('foo%s', 'bar')
        var cmd = pattern.split("%s").join(plugin);
        exec(cmd, function (error, stdout, stderr) {
            grunt.log.writeln('Installing WordPress Plugin: "' + plugin + '"');
            grunt.log.writeln(stdout);
            done(error);
        });
    });
});

Your problem here is you are calling done (aka this.async()) each time through the loop but grunt doesn't know you have a loop. You need to have your own callback which tracks when all of the async things are done and then only once call grunt's this.async().

Alternatively, searching the web for "node exec synchronous" will turn up plenty of ways to achieve that task if it's what you want.

You are calling done before it gets stdout message. Listen on events to get notify when it is actually done... something along these lines:

    exec(cmd, function (error, stdout, stderr) {
        stdout.on('data', function(data){
          grunt.log.writeln('Installing WordPress Plugin: "' + plugin + '"');
          grunt.log.writeln(stdout);
          done();
        });

        stderr.on('data', function(err){
          done(err);
        });
    });