Hacer para cada uno asíncrono en JavaScript

Estoy tratando de entender la progtwigción asíncrona Node.js pero estancado en este código.

Esta función en su callback devuelve una matriz de archivos en un directorio:

function openDir(path, callback) { path = __dirname + path; fs.exists(path, function (exists) { if (exists) { fs.readdir(path, function (err, files) { if (err) { throw err; } var result = []; files.forEach(function (filename, index) { result[index] = filename; }); return callback(result); }); } }); } 

Pero cuando uso código asíncrono dentro de .forEach , no devuelve nada:

 function openDir(path, callback) { path = __dirname + path; fs.exists(path, function (exists) { if (exists) { fs.readdir(path, function (err, files) { if (err) { throw err; } var result = []; files.forEach(function (filename, index) { fs.stat(path + filename, function (err, stats) { if (err) { throw err; } result[index] = filename; }); }); return callback(result); }); } }); } 

Entiendo por qué sucede, pero no entiendo cómo escribir el código correcto.

Las otras respuestas pueden funcionar bien, pero actualmente son bastante diferentes semánticamente del código original: ambas ejecutan stats en paralelo, en lugar de secuencialmente. forEach iniciará tantas operaciones stats asíncronas como archivos en la lista de archivos. El orden de finalización de esas operaciones puede ser bastante diferente del orden original de la lista. Esto puede afectar sustancialmente la lógica de manejo de errores.

El siguiente enfoque implementa una máquina de estado, cuyo objective es ejecutar stats forma asíncrona, pero secuencial (sin probar):

 function openDir(path, callback) { path = __dirname + path; fs.exists(path, function (exists) { if (!exists) callback(null, null); // node (err, result) convention else { fs.readdir(path, function (err, files) { if (err) callback(err, null); // node (err, result) convention else { var results = []; var i = 0; nextStep(); // process the first file (the first step) function nextStep() { if (i >= files.length) // no more files? callback(null, result); // node (err, result) convention else { fs.stat(path + files[i], function (err, stats) { if (err) callback(err, null); // node (err, result) convention else { results[i++] = stats; // proceed to the next file nextStep(); } }); } } } } } }); }); 

Las promesas pueden ayudar a reducir el nivel de anidación de la famosa “Pirámide de la Muerte” como se muestra arriba.

El problema es que fs.stat también es asíncrono, pero probablemente podrías hacer algo como:

 var result = [], expectedLoadCount = files.length, loadCount = 0; files.forEach(function (filename, index) { fs.stat(path + filename, function (err, stats) { if (err) { throw err; } result[index] = filename; if (++loadCount === expectedLoadCount) callback(result); }); }); 

prueba esto:

 function openDir(path, callback) { path = __dirname + path; fs.exists(path, function (exists) { var totalFiles = 0;; if (exists) { fs.readdir(path, function (err, files) { if (err) { throw err; } var result = []; files.forEach(function (filename, index) { fs.stat(path + filename, function (err, stats) { if (err) { throw err; } result[index] = filename; totalFiles++; if(totalFiles === files.length){ callback(result); } }); }); }); } }); } 

También puede utilizar el módulo Async , para ayudar en este tipo de situaciones.