1

i am building validation for one of form's field serverside (expressjs) and doing following actions for that:

  1. Read data from json file

  2. Get property from it (Array)

  3. Check if it contains every single element of user generated array and nothing more, for example:

[1,2,3,4,5]; (json array)
[1,2,3,4,5,6] (user generated array) //must return false

[1,2,3,4,5];
[1,3,4] //must return true;

[1,2,3,4,5];
[1,2,7] //must return false;

so i am using this code for that:

const contains = (arr1, arr2) => {
  arr2.every(v => arr1.indexOf(v) !== -1)
}
var match;
fs.readFile('../tags.json', 'utf8', (err, data)=>{

  var JsonData = JSON.parse(data);
  var tagsArray = JsonData.tags;
  console.log(tagsArray)
  console.log(tags)
  if(tagsArray instanceof Array){
    console.log('tagsArray is array')
  }
  if(!contains(tagsArray, tags)){
    match = false
  }   
  else{
    match = true
  }
  console.log(match + ' blah1')

});

console.log(match + ' blah2')
if(match == false){
  return res.status(409).send({
    message: 'Do not provide your own tags'
  });
}

but it always returns false inside fs.readFile block because it returns undefined outside fs.readFile block, so this means that contains function return undefined (i tested it)

so what is the clue for this? Thanks!

1565986223
  • 6,420
  • 2
  • 20
  • 33
iLiA
  • 3,053
  • 4
  • 23
  • 45
  • 2
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – eol May 18 '19 at 16:51

2 Answers2

1

fs.readFile is asynchronous, so any code that depends on its result (the file being read) needs to go within your callback function. (The callback function is the (err, data) => { ... } part.)

Move the console.log(match + 'blah2') and if(match == false) { ... } parts inside of the callback (after the blah1 line).

You could also look into async or use fs.readFileSync which would allow you to avoid using callback functions.

Another side point, you will want to make sure you always reach a res.send() line, i.e. when match == true in your case. Otherwise your http request will not return when match is true.

Edit:

Here's a really basic structure for express, mostly pseudocode & comments, just to illustrate callbacks:

app.post('/tags', (req, res) => {

  // your setup code here

  fs.readFile('../tags.json', 'utf8', (err, data) => {

    console.log('readFile has finished')

    // now you have heard back from readFile
    // check the err and send 500 if there was a problem
    // otherwise work with the file in the var data

    // any other db-related stuff also goes in here, which probably
    //   has its own callback you need to use
    db.save(data, (err) => {
      // db call is done, potentially with an error
      // Here you can use `res` to send http response
    })
    // !! again here the db is still doing its work
  })

  // !! anything you add here will be executed before readFile is done
  console.log('readFile is probably still at work')

})

I should also point out that you want contains to return the bool value, i.e. return arr2.every(...)

csum
  • 1,782
  • 13
  • 15
  • if i move if statement inside callback function it causes error: Cant set headers after they are sent – iLiA May 18 '19 at 17:04
  • and since, i am not a big fan of synchronous functions because it blocks thread, i want asynchronous solution, but by the way in this case which one is most preferable (i know it is optional but which one would you use if you headed to this problem for example)? i am saying that because i am heading to this kind of problem for the first time – iLiA May 18 '19 at 17:07
  • The headers error is likely a separate issue. I'm able to get things to work by moving those 6 lines into the callback. Re: a/sync, your general approach is good. See the link provided by @eol to read more about all how it all works. – csum May 18 '19 at 17:34
  • yes you can but i can not because this function is inside router.post and i send status code after this block so it is causing errors – iLiA May 18 '19 at 18:35
  • yes exactly, do not send status code outside the callback. – csum May 18 '19 at 19:12
  • but after fs.readFile i need to save data in database and etc. and is it good practice to move all that inside the callback function? – iLiA May 18 '19 at 19:15
  • That's what you have to do, it's the nature of asynchronous functions. Anything at all -- db writes, sending the final http response -- that needs to happen after the file is read must go in the callback. eol's comment points to a similar post, and there is a link within that as well that dives into the world of callbacks/asynchronous functions. Basically, the callback you give to readFile *is* after `readFile`. Anything outside the callback will be executed *while* `readFile` is doing its work. Hence the errors. – csum May 18 '19 at 19:20
  • i see you are experienced as well in this term, what would you do, block a thread or move whole db writes and sending final http response in callback – iLiA May 18 '19 at 19:27
0

You can use async/await :

async function read(){
let data = await fs.readFile(<path>);
console.log(data); //You can use data everywhere within this scope
}
Le Quang
  • 515
  • 4
  • 10