Exécution d'appels asynchrones de manière synchrone
J'essaie de comprendre ce problème depuis quelques heures, mais je n'arrive pas à le comprendre. Je suppose que je dois encore m'habituer au style de programmation fonctionnel;)
J'ai écrit une fonction récursive qui traverse une structure de répertoires et fait des choses sur certains fichiers. Cette fonction utilise les méthodes asynchrones IO. Maintenant, je veux effectuer une action lorsque toute la traversée est terminée.
Comment puis-je m'assurer que cette action est effectuée après que tous les appels parse
ont été effectués, mais que j'utilise toujours les fonctions asynchrones IO?
var fs = require('fs'),
path = require('path');
function parse(dir) {
fs.readdir(dir, function (err, files) {
if (err) {
console.error(err);
} else {
// f = filename, p = path
var each = function (f, p) {
return function (err, stats) {
if (err) {
console.error(err);
} else {
if (stats.isDirectory()) {
parse(p);
} else if (stats.isFile()) {
// do some stuff
}
}
};
};
var i;
for (i = 0; i < files.length; i++) {
var f = files[i];
var p = path.join(dir, f);
fs.stat(p, each(f, p));
}
}
});
}
parse('.');
// do some stuff here when async parse completely finished
Recherchez module Step . Il peut enchaîner les appels de fonctions asynchrones et transmettre les résultats de l'un à l'autre.
Vous pouvez utiliser le module asynchrone. Sa fonction automatique est impressionnante. Si vous avez la fonction A() et la fonction B() et la fonction C(). Les deux fonctions B() et C() dépendent de la fonction A() qui utilise le retour de valeur de la fonction A(). en utilisant la fonction du module asynchrone, vous pouvez vous assurer que les fonctions B et C ne s'exécuteront que lorsque l'exécution de la fonction A sera terminée.
Réf: https://github.com/caolan/async
async.auto({
A: functionA(){//code here },
B: ['A',functionB(){//code here }],
C: ['A',functionC(){//code here }],
D: [ 'B','C',functionD(){//code here }]
}, function (err, results) {
//results is an array that contains the results of all the function defined and executed by async module
// if there is an error executing any of the function defined in the async then error will be sent to err and as soon as err will be produced execution of other function will be terminated
}
})
});
Dans l'exemple ci-dessus, functionB et functionC s'exécuteront une fois l'exécution de la fonction A terminée. Ainsi, functionB et functionC seront exécutées simultanément
functionB: ['A',functionB(){//code here }]
Dans la ligne ci-dessus, nous transmettons le retour de valeur par la fonction A en utilisant 'A'
et functionD ne sera exécuté que lorsque l'exécution de functionB et functionC sera terminée.
s'il y a une erreur dans une fonction, alors l'exécution d'une autre fonction sera terminée et la fonction ci-dessous sera exécutée. Où vous pourriez écrire votre logique de réussite et d'échec.
function (err, results) {}
En cas d'exécution réussie de toutes les fonctions, les "résultats" contiendront le résultat de toutes les fonctions définies dans async.auto
function (err, results) {}
Jetez un œil à la modification de votre code d'origine qui fait ce que vous voulez sans les bibliothèques d'assistance asynchrone.
var fs = require('fs'),
path = require('path');
function do_stuff(name, cb)
{
console.log(name);
cb();
}
function parse(dir, cb) {
fs.readdir(dir, function (err, files) {
if (err) {
cb(err);
} else {
// cb_n creates a closure
// which counts its invocations and calls callback on nth
var n = files.length;
var cb_n = function(callback)
{
return function() {
--n || callback();
}
}
// inside 'each' we have exactly n cb_n(cb) calls
// when all files and dirs on current level are proccessed,
// parent cb is called
// f = filename, p = path
var each = function (f, p) {
return function (err, stats) {
if (err) {
cb(err);
} else {
if (stats.isDirectory()) {
parse(p, cb_n(cb));
} else if (stats.isFile()) {
do_stuff(p+f, cb_n(cb));
// if do_stuff does not have async
// calls inself it might be easier
// to replace line above with
// do_stuff(p+f); cb_n(cb)();
}
}
};
};
var i;
for (i = 0; i < files.length; i++) {
var f = files[i];
var p = path.join(dir, f);
fs.stat(p, each(f, p));
}
}
});
}
parse('.', function()
{
// do some stuff here when async parse completely finished
console.log('done!!!');
});
Quelque chose comme ça fonctionnerait - le changement de base de votre code est la boucle transformée en un appel récursif qui consomme une liste jusqu'à ce qu'il soit fait. Cela permet d'ajouter un rappel externe (où vous pouvez effectuer un traitement une fois l'analyse terminée).
var fs = require('fs'),
path = require('path');
function parse(dir, cb) {
fs.readdir(dir, function (err, files) {
if (err)
cb(err);
else
handleFiles(dir, files, cb);
});
}
function handleFiles(dir, files, cb){
var file = files.shift();
if (file){
var p = path.join(dir, file);
fs.stat(p, function(err, stats){
if (err)
cb(err);
else{
if (stats.isDirectory())
parse(p, function(err){
if (err)
cb(err);
else
handleFiles(dir, files, cb);
});
else if (stats.isFile()){
console.log(p);
handleFiles(dir, files, cb);
}
}
})
} else {
cb();
}
}
parse('.', function(err){
if (err)
console.error(err);
else {
console.log('do something else');
}
});
J'utilise syncrhonize.js avec beaucoup de succès. Il y a même une demande d'extraction en attente (qui fonctionne assez bien) pour prendre en charge les fonctions asynchrones qui ont plusieurs paramètres. Beaucoup mieux et plus facile à utiliser que la synchronisation de nœuds à mon humble avis. Bonus supplémentaire: il dispose d'une documentation facile à comprendre et approfondie, contrairement à la synchronisation des nœuds.
Prend en charge deux méthodes différentes pour câbler la synchronisation, un modèle différé/en attente (comme ce que @Mariusz Nowak suggérait) et une approche de fonction-cible plus mince mais pas aussi granulaire. Les documents sont assez simples pour chacun.
Voir la solution suivante, il utilise le module différé :
var fs = require('fs')
, join = require('path').join
, promisify = require('deferred').promisify
, readdir = promisify(fs.readdir), stat = promisify(fs.stat);
function parse (dir) {
return readdir(dir).map(function (f) {
return stat(join(dir, f))(function (stats) {
if (stats.isDirectory()) {
return parse(dir);
} else {
// do some stuff
}
});
});
};
parse('.').done(function (result) {
// do some stuff here when async parse completely finished
});
Recherchez node-sync , une bibliothèque simple qui vous permet d'appeler n'importe quelle fonction asynchrone de manière synchrone. Le principal avantage est qu'il utilise une conception native javascript - fonction Function.prototype.sync, au lieu des API lourdes que vous devrez apprendre. De plus, la fonction asynchrone qui a été appelée de manière synchrone via node-sync ne bloque pas tout le processus - elle bloque uniquement le thread actuel!
Recommander d'utiliser node-seq https://github.com/substack/node-seq
installé par npm.
Je l'utilise et je l'adore ..