nodejs expresa fs iterando archivos en una matriz u objeto que falla

Así que estoy tratando de usar el módulo nodejs express FS para iterar un directorio en mi aplicación, almacenar cada nombre de archivo en una matriz, que puedo pasar a mi vista expresa e iterar a través de la lista, pero estoy luchando para hacerlo. Cuando hago una console.log dentro del archivo files.forEach function loop, está imprimiendo el nombre del archivo muy bien, pero tan pronto como bash hacer algo como:

var myfiles = []; var fs = require('fs'); fs.readdir('./myfiles/', function (err, files) { if (err) throw err; files.forEach( function (file) { myfiles.push(file); }); }); console.log(myfiles); 

falla, solo registra un objeto vacío. Así que no estoy seguro de lo que está sucediendo, creo que tiene que ver con las funciones de callback, pero si alguien pudiera explicarme qué estoy haciendo mal y por qué no funciona (y cómo hacerlo funcionar), sería muy apreciado.

La matriz myfiles está vacía porque no se ha llamado a la callback antes de llamar a console.log ().

Tendrás que hacer algo como:

 var fs = require('fs'); fs.readdir('./myfiles/',function(err,files){ if(err) throw err; files.forEach(function(file){ // do something with each file HERE! }); }); // because trying to do something with files here won't work because // the callback hasn't fired yet. 

Recuerde, todo en el nodo sucede al mismo tiempo, en el sentido de que, a menos que esté realizando el procesamiento dentro de sus devoluciones de llamada, no puede garantizar que las funciones asíncronas se hayan completado aún.

Una forma de evitar este problema para usted sería usar un EventEmitter:

 var fs=require('fs'), EventEmitter=require('events').EventEmitter, filesEE=new EventEmitter(), myfiles=[]; // this event will be called when all files have been added to myfiles filesEE.on('files_ready',function(){ console.dir(myfiles); }); // read all files from current directory fs.readdir('.',function(err,files){ if(err) throw err; files.forEach(function(file){ myfiles.push(file); }); filesEE.emit('files_ready'); // trigger files_ready event }); 

fs.readdir es asíncrono (como fs.readdir con muchas operaciones en node.js). Esto significa que la línea readdir se ejecutará antes de que readdir tenga la oportunidad de llamar a la función que se le haya pasado.

Usted necesita:

Coloque la línea console.log dentro de la función de callback dada a readdir , es decir:

 fs.readdir('./myfiles/', function (err, files) { if (err) throw err; files.forEach( function (file) { myfiles.push(file); }); console.log(myfiles); }); 

O simplemente realice alguna acción con cada archivo dentro de forEach .

Como han mencionado varios, está utilizando un método asíncrono, por lo que tiene una ruta de ejecución no determinista.

Sin embargo, hay una manera fácil de evitar esto. Simplemente usa la versión Sync del método:

 var myfiles = []; var fs = require('fs'); var arrayOfFiles = fs.readdirSync('./myfiles/'); //Yes, the following is not super-smart, but you might want to process the files. This is how: arrayOfFiles.forEach( function (file) { myfiles.push(file); }); console.log(myfiles); 

Eso debería funcionar como quieras. Sin embargo, el uso de sentencias de sincronización no es bueno, por lo que no debe hacerlo a menos que sea de vital importancia para que se sincronice.

Lea más aquí: fs.readdirSync

Creo que tiene que ver con las funciones de callback,

Exactamente.

fs.readdir realiza una solicitud asíncrona al sistema de archivos para esa información y llama a la callback más tarde con los resultados.

Entonces la function (err, files) { ... } no se ejecuta inmediatamente, pero la console.log(myfiles) sí lo hace.

En algún momento posterior, myfiles contendrá la información deseada.

Debe tener en cuenta que, por cierto, los files ya son una matriz, por lo que realmente no tiene sentido agregar manualmente cada elemento a otra matriz en blanco. Si la idea es juntar los resultados de varias llamadas, entonces use .concat ; Si solo desea obtener los datos una vez, puede asignar myfiles = files directamente.

En general, deberías leer sobre “Estilo de aprobación” .

Enfrenté el mismo problema, y ​​basándome en las respuestas que se dieron en este post, lo resolví con Promesas , que parecen ser perfectamente útiles en esta situación:

 router.get('/', (req, res) => { var viewBag = {}; // It's just my little habit from .NET MVC ;) var readFiles = new Promise((resolve, reject) => { fs.readdir('./myfiles/',(err,files) => { if(err) { reject(err); } else { resolve(files); } }); }); // showcase just in case you will need to implement more async operations before route will response var anotherPromise = new Promise((resolve, reject) => { doAsyncStuff((err, anotherResult) => { if(err) { reject(err); } else { resolve(anotherResult); } }); }); Promise.all([readFiles, anotherPromise]).then((values) => { viewBag.files = values[0]; viewBag.otherStuff = values[1]; console.log(viewBag.files); // logs eg [ 'file.txt' ] res.render('your_view', viewBag); }).catch((errors) => { res.render('your_view',{errors:errors}); // you can use 'errors' property to render errors in view or implement different error handling schema }); }); 

Nota: no tiene que insertar los archivos encontrados en una nueva matriz porque ya obtiene una matriz de la callback fs.readdir () ‘c. Según los documentos del nodo:

La callback obtiene dos argumentos (error, archivos) donde los archivos son una matriz de los nombres de los archivos en el directorio excluyendo ‘.’ y ‘..’.

Creo que esta es una solución muy elegante y práctica, y sobre todo, no requiere que traigas y manejes nuevos módulos a tu script.