2011-07-10 3 views
6

J'ai le code suivant dans une classe. (C'est coffeescript - et c'est pour un utilitaire couchdb! - mais c'est vraiment une question node.js). J'essaye de faire des choses au niveau du nœud, en utilisant le nœud 0.49, et cela signifie utiliser des appels asynchrones pour les opérations du système de fichiers. Au début, je tirais mes cheveux parce que this.sentinel est allé à zéro plusieurs fois au cours du traitement, donc je sais que je fais quelque chose de mal là-bas. Mais alors je suis tombé sur un problème encore plus bizarre: down dans load_directory, voir ces console.log() appels? Regarde quand ça se passe quand je cours ça.Node.js et le système de fichiers: est-ce une condition de concurrence?

check_sentinel: -> 
    @sentinel-- 
    if @sentinel == 0 
     @emit('designDirLoaded', @object) 

load_file: (rootdir, filename, object) -> 
    @sentinel++ 
    fname = path.join(rootdir, filename) 
    @manifest.push(fname) 
    fs.readFile fname, (err, data) => 
     object[filename] = data 
     @check_sentinel() 

load_directory: (dirpath, object) -> 
    @sentinel++ 
    fs.readdir dirpath, (err, files) => 
     for fname in files 
      console.log("X1: ", fname) 
      fs.stat path.join(dirpath, fname), (err, stats) => 
       console.log("X2: ", fname) 
       if stats.isFile() 
        @load_file(dirpath, fname, object) 
       if stats.isDirectory() 
        object[fname] = {} 
        @load_directory(path.join(dirpath, fname), object[fname]) 
     @check_sentinel() 

Voici ce que je reçois:

X1: memberByName.js 
X1: memberByClub.js 
X2: memberByClub.js 
X2: memberByClub.js 

C'est surréaliste, et il semble beaucoup comme une condition de course. "memberByName" est passé à fs.stat(), qui à son tour passe "memberByClub" à load_file(), ce qui implique ... quoi? Cela parce que load_file() retourné immédiatement, il a couru autour et présenté le nom de fichier suivant dans le tableau à l'appel de la fonction? Ou ai-je un malentendu sur la persistance des valeurs dans un périmètre donné?

Répondre

8

Non, ce que vous voyez est attendu. Une chose que vous devez vous rappeler est que fs.stat est asynchrone. Ainsi, la boucle externe (for fname in files) terminera la boucle avant que l'un des rappels au fs.stat ne soit appelé.

La raison pour laquelle vous voyez memberByClub.js est deux fois plus que vous utilisez fname dans la déclaration d'enregistrement, mais cette variable est de la fermeture, ce qui a changé au moment où votre rappel à fs.stat est appelée.

Vous pouvez envelopper la boucle interne avec do (fname) => pour obtenir les instructions de journalisation correctes, mais je pense que vous devez restructurer votre code pour réaliser ce que vous essayez de faire avec toute la classe.

load_directory: (dirpath, object) -> 
    @sentinel++ 
    fs.readdir dirpath, (err, files) => 
     for fname in files 
      do (fname) => 
       console.log("X1: ", fname) 
       fs.stat path.join(dirpath, fname), (err, stats) => 
        console.log("X2: ", fname) 
        if stats.isFile() 
         @load_file(dirpath, fname, object) 
        if stats.isDirectory() 
         object[fname] = {} 
         @load_directory(path.join(dirpath, fname), object[fname]) 
     @check_sentinel() 
+0

Merci. C'était * mon malentendu l'interaction de la portée et de l'asynchronie. C'est drôle comme je n'ai jamais rien vu de tel dans les années de programmation côté client. Et j'arrive à ajouter l'opérateur 'do' à mon arsenal de coffeescript. –

Questions connexes