2011-03-15 9 views
27

He estado empezando a jugar con Node.js recientemente y he encontrado una situación en la que necesito una pequeña guía sobre la forma prescriptiva de node.js de llevar a cabo una tarea. En este caso particular, necesito crear un grupo de directorios y, cuando se hayan creado todos los directorios, necesito realizar alguna operación final. El orden en que se crean los directorios no importa, solo necesito realizar una operación final después de la última.Node.js prescriptivo

La manera más fácil de lograr esto sería recurrir a los viejos hábitos sincrónicos. Es decir, simplemente llame al fs.mkdirSync para cada uno de los directorios y realice la operación al final. Por ejemplo:

fs.mkdirSync('a', 0755); 
fs.mkdirSync('a/b', 0755); 
fs.mkdirSync('a/b/c', 0755); 
performFinalOperation(); 

Si bien esto funcionaría, no parece que sea la forma de node.js de hacerlo. Obviamente, el programa se bloquearía mientras espera que el sistema operativo cree el directorio y lo devuelva. En un sistema muy cargado con un sistema de archivos montado de forma remota, cada una de las llamadas mkdirSync podría llevar mucho tiempo. Entonces, claramente, este no es el mejor enfoque.

Uno de los principales puntos de venta de Node.js es el hecho de que es asincrónico. Por lo que las llamadas a fs.mkdir podrían encadenarse a través de las devoluciones de llamada:

fs.mkdir('a', 0755, function(e) { 
    if (!e) { 
     fs.mkdir('a/b', 0755, function(e) { 
      if (!e) { 
       fs.mkdir('a/b/c', 0755, function(e) { 
        if (!e) { 
         performFinalOperation(); 
        } 
       }); 
      } 
     }); 
    } 
}); 

Una vez más, este enfoque Estoy seguro que funciona, pero conduce a la anidación muy profunda y la duplicación de código. Tiene el beneficio de no bloquear mientras se crean los directorios, pero ¿a qué costo?

Otro enfoque sería la de obtener realmente de lujo en un esfuerzo por evitar la duplicación de código y de anidación:

(function (directories) { 
    if (directories.length === 0) { 
     performFinalOperation(); 
    } else { 
     var tail = arguments.callee; 
     fs.mkdir(directories.shift(), 0755, function(e) { 
      tail(directories); 
     }); 
    } 
})(['a', 'a/b', 'a/b/c']); 

Este enfoque hace uso de todo tipo de cosas locas: las funciones de llamada auto anónima y los argumentos mágicos. Llamado Pero lo peor de todo es que no es obvio lo que el código está haciendo a primera vista.

Por lo tanto, si bien la pregunta concreta es sobre la creación de directorios, estoy más interesado en el enfoque que un experimentado veterano node.js tomaría cuando surja este tipo de situación. Estoy específicamente no interesado en qué bibliotecas hay para facilitar esto.

+0

Usaría Deferreds o crearía una función de ayuda que se llama a sí misma de forma recursiva siempre que haya carpetas restantes para crear. – Tower

+0

Oye, si estuvieras buscando una utilidad que lo haga por ti, usarías 'async'. Se necesita el dolor de lidiar con el modelo asincrónico. Es mejor separar las tareas y no reinventar la rueda. IMHO :) –

Respuesta

6

Oh, hey Bryan :)

que específicamente no estoy interesado en lo que las bibliotecas están cerca para hacer de este más fácil.

El experimentado veterano de Node ha escrito al menos una de sus propias librerías de flujo de control. Acabamos de copiar las clases de Twisted Deferred ya que ya hicieron el trabajo duro e investigaron la programación asíncrona. Esto invierte el patrón estándar de devolución de llamada como argumento y me gusta el código resultante, pero si desea anidar un grupo de devoluciones de llamadas, puede hacerlo con Deferred y terminar con un desastre.

Con la restricción de no usar bibliotecas, la gente generalmente hace exactamente lo que usted escribió. Realmente no hay otra opción. Sin cambios en el lenguaje, como generadores, lo mejor que podemos hacer es utilizar bibliotecas. Si no quiere usar uno ya existente, terminará enrollando el suyo o simplemente escribiendo un montón de texto estándar.

+2

+1 uno de los mejores elementos que tiene el nodo es su ecosistema de módulo. El núcleo se mantiene razonablemente pequeño (no resuelve todos los problemas), por lo que los módulos de usuario pueden resolver este tipo de problemas. Esto mantiene el nodo ligero, ágil y en constante evolución. – timoxley

1

Creo que la manera de hacerlo es con un contador. Comience con la cantidad de directorios que debe crear y luego disminuya cuando finalice cada operación. Pruebe también cero en este punto y realice la operación final.

Ver http://howtonode.org/control-flow-part-iii

+0

Esto es ideal para tareas que pueden ejecutarse en paralelo, no es la mejor solución cuando las tareas se deben ejecutar de forma secuencial (aquí no hay necesidad de ningún contador). – Adrien

19

Su segunda solución se puede simplificar en gran medida, y los errores como este:

var mkdirs = function(dirs, mode, cb){ 
    (function next(e) { 
    (!e && dirs.length) ? fs.mkdir(dirs.shift(), mode, next) : cb(e); 
    })(null); 
}; 
+0

Esto me llevó un tiempo entenderlo porque nunca uso el operador '?:' Con efectos secundarios como ese. ¿Tal vez una solución tradicional basada en 'si' sería más clara? – strager

+0

+1, pero sería bueno si puede tener un poco más de explicación de lo que está sucediendo aquí? – RobertPitt

+1

Para mí, esto se parece más al código de golf que a la simplificación de cualquier cosa. –

0

Hm, esto es especulación, y casi no conozco ningún Javascript, pero ¿qué tal algo con un resultado final de esta manera?

Asynchronously 
    .Do(function(callback) { 
    fs.mkdir('a', 0755, callback); 
    }) 
    .Then(function(result, callback) { 
    if(!result) 
     fs.mkdir('a/b', 0755, callback); 
    }) 
    .Then(function(result, callback) { 
    if(!result) 
     fs.mkdir('a/b/c', 0755, callback); 
    }) 
    .Then(function(result, callback) { 
    if(!result) 
     performFinalOperation(); 
    }); 
9

npm install mkdirp

var mkdirp = require('mkdirp').mkdirp; 

mkdirp('/tmp/foo/bar/baz', 0755, function (err) { 
    if (err) console.error(err) 
    else console.log('pow!') 
}); 
+0

Jaja, eso también es una opción: P ... – Alfred

+0

Hola, subbase. mkdirp es genial, pero creo que OP está pidiendo una solución más general (Prescriptive JS). Los directorios son solo un ejemplo de tal caso. –

0
  • Los veteranos escribió bibliotecas para simplificar esto. No quiere saber nada sobre esto, así que no voy a entrar en detalles, pero realmente me gusta async.js.
  • También esta publicación (video) de Yahoo! es realmente interesante para avoid writting spaghetti-code
0

Recomendaría mirar en Step. Hace la transición de sincrónico a asíncrono mucho más fácil.

0

Qué tal este enfoque. Creo que está bastante claro, maneja los errores, y no hace uso de arguments.callee que es más mágico de lo que generalmente me siento cómodo.

var fs = require('fs'); 

function mkdirs(dirs, cb, err) { 
    if (err) { 
     return cb(err); 
    } 

    if (dirs.length === 0) { 
     return cb(); 
    } 

    var dir = dirs.shift(); 
    fs.mkdir(dir, mkdirs.bind(this, dirs, cb)); 
} 

// Test it. 
mkdirs(['a', 'a/b', 'a/b/c'], function (e) { 
    if (e) { 
     return console.log("An error:", e); 
    } 

    console.log("No error."); 
});