sword1st
sword1st

Reputation: 555

Sending POST response from a Variable using Express.js

I'm trying to list files sending a POST request, but I can't figure out what's wrong with what I am doing. Here's my function:

app.post('/api/list', function(req,res){
   listFiles();
   res.send(files);
});

And here is my listFiles function and test variable :

var files = "";
var listFiles = function(){
   fs.readdir(testFolder, (err, files) => {
      files.forEach(file => {
      files += file;
   });
   console.log(files);
  })
};

I can see the paths with a console.log output. But, when I use res.send(files); in the POST, the files aren't shown - it's empty. Here's what I've written in jQuery to show the files:

$('li').on('click', function(){
  var item = $(this).text().replace(/ /g, "-");
  $.ajax({
    type: 'POST',
    url: '/api/list',
    success: function(data){
      //do something with the data via front-end framework

      alert('response : ' + data);
      //location.reload();
    }
  });
});

My connection is okay if I send res.send("test");. I can make that alert message as "response : test". But when I try to send a variable it's empty.

Upvotes: 2

Views: 984

Answers (1)

Dom
Dom

Reputation: 1816

Welcome to async programming! :)

fs.readdir works asynchronously, so when you call listFiles(), that returns immediately and goes on to the next line - res.send(files). At that moment files really still is an empty string, so that's what you're seeing.

The callback of fs.readdir is actually executed after res.send(files) finishes, so you would either need to execute res.send() in the same fs.readdir callback, or control the flow with Promises.

Here are a few examples:

  1. Pass res to listFiles, execute res.send() after you have the output you need

    var listFiles = function (res) { fs.readdir(..., (err, files) => { var filesString = ""; files.forEach(...) res.send(filesString) }) };
    app.post('/api/list', function(req,res){ listFiles(res); });

  2. Pass a callback to listFiles, similar to the first one but following the same pattern fs.readdir uses

    var listFiles = function (callback) { fs.readdir(..., (err, files) => { var filesString = ""; files.forEach(...) callback(filesString) }) };
    app.post('/api/list', function(req,res){ listFiles((filesString) => res.send(filesString)); });

  3. Wrap fs.readdir in a Promise (you can also use util.promisify for this, check out the Node docs)

    var listFiles = new Promise((resolve, reject) => { fs.readdir(..., (err, files) => { var filesString = ""; files.forEach(...) return resolve(filesString) }) })
    app.post('/api/list', function(req,res){ listFiles().then((filesString) => res.send(filesString)) // or, even better as Express can reply with a Promise directly // res.send(listFiles()) });

There's a nice series about NodeJS, async programming and the event loop, I recommend you to read up on it so you get the hang of the bigger picture: https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810. Also, check out the stuff related to async/await, it makes Promises that much nicer to work with in most cases.

Upvotes: 2

Related Questions