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