2011-07-10 9 views
6

Tengo el siguiente código dentro de una clase. (Es coffeescript-- ¡y es para una utilidad couchdb! - pero esta es realmente una pregunta de node.js). Intento hacer The Node Way, usando Node 0.49, y eso significa usar llamadas asincrónicas para las operaciones del sistema de archivos. Al principio, me estaba tirando de los pelos porque this.sentinel se fue a cero varias veces durante el curso del procesamiento, así que sé que estoy haciendo algo mal allí. Pero luego llegué a un problema aún más extraño: abajo en load_directory, ¿ves esas llamadas console.log()? Mira cuando sucede cuando ejecuto esto.Node.js y el sistema de archivos: ¿es esta una condición de carrera?

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() 

Esto es lo que me sale:

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

Esto es surrealista, y se ve mucho a una condición de carrera. "memberByName" pasa a fs.stat(), que a su vez pasa "memberByClub" al load_file(), lo que implica ... ¿qué? ¿Eso porque load_file() volvió inmediatamente, corrió y presentó el siguiente nombre de archivo en la matriz a la llamada a la función? ¿O tengo algún malentendido acerca de la persistencia de los valores en un ámbito determinado?

Respuesta

8

No, lo que ves es esperado. Una cosa que debes recordar es que fs.stat es asincrónico. Por lo tanto, el bucle externo (for fname in files) finalizará el bucle antes de llamar a cualquiera de las devoluciones de llamada al fs.stat.

La razón por la que se ve el doble memberByClub.js es que está utilizando fname en la declaración de registro, pero esa variable es del cierre, lo que ha cambiado en el momento de su devolución de llamada a fs.stat se llama.

Puede envolver el bucle interno con do (fname) => para obtener las declaraciones de registro correctas, pero creo que debe reestructurar su código para lograr lo que está tratando de hacer con toda la clase.

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

Gracias. Fue * mi malentendido la interacción del alcance y la asincronía. Es curioso cómo nunca he golpeado algo así en años de programación del lado del cliente. Y puedo agregar el operador 'do' a mi arsenal de coffeescript. –

Cuestiones relacionadas