Función que se comporta de forma asíncrona incluso después de una función de callback en Node.js

Estoy intentando hacer un explorador de archivos usando el módulo ‘fs‘ de Node.js.

Escribí la siguiente función que toma una ruta y almacena el contenido de esa ruta (archivos / carpetas) en una matriz y la devuelve con la callback.

listDir: function (path, myCallback) { var resultObj = []; fs.readdir(path, function (err, data) { console.log('In listDir'); if (err) { return myCallback(err, null); } else { data.forEach(function (value) { fs.lstat(path + '/' + value, function (err, stats) { if (err) { console.log(err); } else { //console.log('Size-' + stats.isFile()+'\n'); if(stats.isFile()){ //console.log('is file----\n'); /*resultObj.push({ type: 'file', name: value, size: stats['size'] });*/ resultObj.push(value); console.log(resultObj+'\n'); } else if(stats.isDirectory()){ //console.log('is folder----\n'); /*resultObj.push({ type: 'folder', name: value, size: stats['size'] });*/ resultObj.push(value); console.log(resultObj+'\n'); } } }); }); console.log('Resultant obj-' + resultObj); return myCallback(null, resultObj); } }); } 

Para cada archivo / carpeta almacenado en ‘datos’, estoy tratando de verificar si es un archivo o una carpeta y, dependiendo de eso, quiero insertar un objeto en mi matriz ‘resultObj’. Sin embargo, la statement de retorno se ejecuta incluso antes de que se complete forEach y, por lo tanto, siempre obtengo un resultObj vacío.

¿Por qué sucede eso? ¿Por qué se comporta de forma asíncrona incluso cuando he proporcionado una función de callback?

La salida es algo como esto cuando llamo a la ruta / test con la ruta param–

En listDir
Obj
GET / test? Path = F: \ dummyFolder 304 15.210 ms – –
archivo1
archivo1, archivo2
archivo1, archivo2, carpeta3

Por favor, ayúdenme con esto, estoy realmente atascado con esta tarea que debe completarse esta noche. Y también se ha mencionado específicamente en la asignación NO UTILIZAR NINGUNA FUNCIÓN SINCRÓNICA DEL MÓDULO ‘fs’ . Entonces, ¿cómo puedo hacer esto teniendo en cuenta esa cláusula?

PD: No me importan los comentarios, los usé para depurar.

El problema es que no estás esperando a que fs.lstat() función fs.lstat() para cada archivo. Estás fs.lstat() cola todas las llamadas fs.lstat() pero luego no esperas a que todas terminen. Solo devolverá resultObj cuando aún esté [] .

Puede solucionar esto agregando un cheque dentro de la callback de sus data.forEach() para ver cuántos de los elementos han tenido la callback para fs.lstat() invocada.

 listDir: (path, myCallback) => { let resultObj = []; fs.readdir(path, (err, data) => { console.log('In listDir'); if (err) { return myCallback(err); } let itemsCompleted = 0 data.forEach(value => { fs.lstat(path + '/' + value, (err, stats) => { itemsCompleted++ if (err) { console.log(err); } else { if(stats.isFile() || stats.isDirectory()){ resultObj.push(value); console.log(resultObj+'\n'); } } if(itemsCompleted >= data.length) { return myCallback(null, resultObj) } }); }); } 

Sin embargo, el enfoque anterior es aún más complicado que las promesas con async/await . Las promesas con async/await son actualmente la forma preferida de manejar el flujo de control asíncrono en Node. El uso de util.promisify() para promisificar fs.readdir() y fs.lstat() nos permite aplanar el flujo de control y mejorar considerablemente la legibilidad de listDir() . Además, al usar map() lugar de forEach() , podemos usar Promise.all() para ajustar la Promesa devuelta de lstatAsync() para cada entrada.

 const {promisify} = require('util') const fs = require('fs') const {join} = require('path') const readdirAsync = promisify(fs.readdir) const lstatAsync = promisify(fs.lstat) module.exports = { listDir: async path => { let resultObj = []; let entries = await readdirAsync(path) console.log('In listDir'); await Promise.all(entries.map(async value => { try { let stats = await lstatAsync(join(path, value)) if(stats.isFile() || stats.isDirectory()){ resultObj.push(value); console.log(resultObj+'\n'); } } catch (err) { console.log(err) } })) return resultObj } }