(Please note this is not a duplicate of two similarly titled questions, those two questions use Mongoose and the answers apply to Mongoose queries only)
I have a list of directories, each of these directories contains a file. I want to return a JSON list with the contents of each of these files. I can load the files no problem, but because I'm looping over the array with forEach
, my empty response is sent before I've actually loaded the contents of the files:
function getInputDirectories() {
return fs.readdirSync(src_path).filter(function(file) {
return fs.statSync(path.join(src_path, file)).isDirectory();
});
}
router.get('/list', function(req, res, next) {
var modules = [];
var input_dirs = getInputDirectories();
input_dirs.forEach(function(dir) {
path = __dirname+'/../../modules/input/'+dir+'/module.json'
fs.readFile(path, 'utf8', function(err, data) {
modules.push(data);
});
});
res.status(200).json(modules);
});
How can I make sure that I only send down the modules
array once it's fully loaded, ie: once the forEach
is done.
Since fs.readFile
is asynchronous, the behaviour that you are having is most likely the expected one.
What you need to do is return your modules when all modules have been read. You could do this inside fs.readFile
.
As far as I have understood, you can obtain the total number of directories through input_dirs.length
(since I guess getInputDirectories()
is returning an array). Now you need some kind of a counter that helps you understand if you have read the last directory or not, and if yes, then you return your modules. Something like this should work:
router.get('/list', function(req, res, next) {
var modules = [];
var input_dirs = getInputDirectories();
var c = 0;
input_dirs.forEach(function(dir) {
path = __dirname+'/../../modules/input/' + dir + '/module.json'
fs.readFile(path, 'utf8', function(err, data) {
c++;
modules.push(data);
if(c == input_dirs.length) {
return res.status(200).json(modules);
}
});
});
});
I suggest you use Promises, example:
var Promise = require('bluebird');
router.get('/list', function(req, res, next) {
var modules = [];
var input_dirs = getInputDirectories();
// 'each' will try to fulfill all promises, if one fails, it returns a
// failed promise.
return Promise.each(input_dirs, function(dir){
path = __dirname+'/../../modules/input/'+dir+'/module.json';
return new Promise(function(resolve, reject){
fs.readFile(path, 'utf8', function(err, data) {
if (err) return reject(err);
return resolve(data);
});
});
}).then(function(modules){
return res.status(200).json(modules);
})
.catch(function(err){
if (err) {
//handle error
}
});
});
This way you move one once you fulfilled your promises.
Instead of fs.readFile
use fs.readFileSync
来源:https://stackoverflow.com/questions/34720197/sending-response-after-foreach