Un tutoriel sur Gulp pour comprendre les dépendances, l’utilisation de tâches asynchrones ou synchrones

J’ai pris de le temps de faire ce tutoriel car je vois trop souvent des incompréhensions sur ces notions de tâches synchrones et asynchrones. En complément je rappelle le principe de dépendances dans gulp. Les dépendances ne sont pas ordonnées, seul la fin de la tâche déclenche l’exécution de la suivante, il faut donc s’assurer que les fins tâches sont déclenchées seulement lorsqu’elles sont effectivement terminés. Ceci amène à la notion de barrière et de promesses (je n’ai pas détaillé les promesses ici, les docs ne manquent pas sur ce sujet).

Au niveau dépendances

Les logs de ‘un’ se mixe avec ‘trois’ en asynchrone (c’est que l’on cherche) mais ‘deux’ ne se lance bien qu’après la fin de ‘trois’.

Mise en œuvre d’une barrière pour gérer correctement les fins de tâches asynchronesIl n’y a pas besoin de mettre en place de section critique en js, chaque fonction possède son propre contexte, donc la barrière qui gère la fin de l’ensemble des process est plutôt simple à mettre en œuvre.

Au niveau perf

En asynchrone je boucle la tâche ‘quatre’ en 3,6 secondes alors qu’en synchrone elle se boucle en 4,4 secondes. Cela commence à faire beaucoup sur si peu d’itérations.

Le code

J’ai commenté en anglais histoire de partager un peu.

var child_process = require('child_process');


function execScriptAsync(script, cb) {
//	var _script=script;
//	var _cb=cb;
    //this function is triggered on script is completed
	function onCompleted(error, stdout, stderr){
		if (error) {
        	console.log("Error on script: "+script+ " error = "+error);
        } else {
            console.log("Execution completed on : "+script);
		    console.log("   stdout: "+stdout);
	        if (stderr) console.log("   stderr: "+stderr);
		}
	    if (cb) {
	        cb(error);
	    }
	} 
	console.log("Executing script : "+script);
	child_process.exec(script, onCompleted);
};

var runnerAsync = function (name, iteration, cb) {
    var i=0;
    var j=iteration;
    // this function is used as a barrier, once all process are completed, trigger the callback so the task will be ended properly.
    var wait = function() {
        j--;
        console.log("   Remaining '"+name+"' process="+j);
        if (j==0) {
            // do not forget to call the callback, gulp is using this to trigger the task ended
            cb();
        }
    }
    for (i=0; i<iteration; i++) {
        execScriptAsync('echo '+name, wait);
        //we are reaching this code even if the echo command is not ended, so we can have several echo called simultaneously
        // the number of echo in parallel depends on how many process can be started simultaneously on the box
    }
    // do not call the callback here otherwise you will have the task completed while 'echo' cmd are still running
};

var runnerSync = function (name, iteration, cb) {
    for (var i=0; i<iteration; i++) {
        var echo = child_process.spawnSync('echo', [name], {shell: true});
        //we are reaching this code only once the echo command is ended
        if (echo && echo.stdout) {
            console.log('stdout: '+echo.stdout);
        }
    }
    cb();
};

// switch from runnerSync to runnerAsync this to see the difference between synchronous and asynchronous
var runner=runnerAsync; // runnerAsync;

gulp.task('one', function(cb) {
    runner('one', 100, cb);
});

gulp.task('two', ['three'],function(cb) {
    runner('two', 100, cb);
});

gulp.task('three', function(cb) {
    runner('three', 100, cb);
});

var Q = require('q');
// keep start time of gulp
var startTime=new Date();
// here we can see dependencies behavior, 'two' should be triggered before 'three', but 'two' depends on 'three' so 'three' comes first.
gulp.task('four', ['one', 'two', 'three'], function() {
    //in this task, I'm using a promise which is a better way to handle async process response
    //(with gulp it is not simple to add functional rules between tasks, everything rely on dependencies)
    var deferred = Q.defer();
    var cb=function(data){
        var end=new Date();
        var elapsed=end.getTime() - startTime.getTime();
        console.log("Total time elapsed="+elapsed+' ms');
        // resolve the task 'four'
        deferred.resolve();
    }
    // start processes
    runner('four', 100, cb);
    // return the promise so gulp will be able to process the task completion properly
    return deferred.promise;
});


how to build multiple cordova applications with different cordova version

instead of installing the cordova client with

 npm install -g cordova   # DON'T USE THIS

You should just install the cordova client locally, the best way is to put the cordova dependency in the file package.json so when you run the npm install in the root folder of your application, it will install the appropriate cordova version related to this application.

 # if you have a well configured package.json
 #
 npm install

or

 # without a package json
 # if you want the cordova version 5.2.0
 #
 npm install cordova@5.2.0

But it is not enough because your path is not up to date so when you run cordova, it is still pointing to the global one. The trick is to run the following command (or add it to your .bashrc) :

 export PATH=./node_modules/cordova/bin:$PATH

then type again

 cordova -version

That’s it ! You are running cordova with local cordova client defined by the developer of the app.

Build and install nodejs, npm and cordova on Linux (debian)

A quick reminder on how to install nodejs, npm and cordova from scratch…

to complete the installation make sure you have sufficient rights to create flles in /usr/lib and /usr/bin (use sudo or su otherwise)

NodeJS

nodejs is a python based http engine

download nodejs sources

./configure
make
make install
NPM

npm is a packet manager (like pear in PHP world)

get sources then

./configure
make install

The make is made by dummies and does not detect already built files so use « make install » directly to avoid building file twice.

Cordova

install cordova using the npm package manager:

npm install -g cordova