Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Grunt wait for a task to finish before running another?

Here's my Gruntfile and the output.

As you can see in the output, there are a couple of issues related to asynchronous tasks:

  1. imagemin is called and the next one comes straight ahead. This makes its output appear in the end of the tasks, what's quite messy;
  2. build, which is a custom task, is using var done = this.async() and calling done() after finishing the command; however, this only works correctly if I run the task alone; running it with another tasks makes it run async too;
  3. With build running later, jasmine has nothing to test and thus is useless.

Is there a way to fix this behavior?

like image 624
igorsantos07 Avatar asked Mar 31 '13 22:03

igorsantos07


People also ask

Is grunt a task runner?

Grunt is a JavaScript task runner, a tool used to automatically perform frequent tasks such as minification, compilation, unit testing, and linting. It uses a command-line interface to run custom tasks defined in a file (known as a Gruntfile). Grunt was created by Ben Alman and is written in Node.

Is grunt a build tool?

Grunt is everywhere. JavaScript projects from jQuery to Twitter Bootstrap use Grunt to convert code, run tests, and produce distributions for production. It's a build tool in the spirit of Make and Rake, but written with modern apps in mind.

What does grunt command do?

When you call the Grunt task runner, it runs whatever Grunt plugins you have specified in your Gruntfile in the order you specify. A Grunt plugin consists of a single task file. That file is essentially nothing but a Node. js script that carries out the task in question.


1 Answers

I believe your problem is with this task:

grunt.registerTask('prepare-dist', 'Creates folders needed for distribution', function() {
            var folders = ['dist/css/images', 'dist/imgs/icons'];
            for (var i in folders) {
                    var done = this.async();
                    grunt.util.spawn({ cmd: 'mkdir', args: ['-p', folders[i]] }, function(e, result) {
                            grunt.log.writeln('Folder created');
                            done();
                    });
            }
    });

If you have multiple folders, both async() and done() will be called multiple times. Async is implemented as a simple flag (true/false) and is meant to be called once. The first done() call allows any follow on tasks to run.

There are many ways to move the calls to async and done out of the loop. A quick google search on something like: nodejs how to callback when a series of async tasks are complete will give you some additional options. A couple of quick (& dirty) examples:

// Using a stack
(function() {
    var work = ['1','2','3','4','5']


    function loop(job) {
        // Do some work here
        setTimeout(function() {
            console.log("work done");

            work.length ? loop(work.shift()) : done();
        }, 500);
    }

    loop(work.shift());

    function done() {
        console.log('all done');
    }
})();

-- or --

// Using a counter (in an object reference)
(function() {
    var counter = { num: 5 }

    function loop() {
        // Do some work here
        setTimeout(function() {
            --counter.num;

            console.log("work done");

            counter.num ? loop() : done();
        }, 500);
    }

    loop();

    function done() {
        console.log('all done');
    }
})();
like image 185
dc5 Avatar answered Sep 25 '22 22:09

dc5