web-dev-qa-db-fra.com

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
30
Sven Jacobs

Recherchez module Step . Il peut enchaîner les appels de fonctions asynchrones et transmettre les résultats de l'un à l'autre.

12
Phillip Kovalev

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) {}
4
Abhimanyu

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!!!');
});
3
Andrey Sidorov

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');
  }
});
1
Geoff Chappell

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.

1
shellscape

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
});
1
Mariusz Nowak

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!

0
Francesco Casula

Recommander d'utiliser node-seq https://github.com/substack/node-seq

installé par npm.

Je l'utilise et je l'adore ..

0
murvinlai