-1

The code below opens a file and reads the contents printing them to the console. I modified it to what I thought would return a string but I get undefined. The goal is not to print the contents but hold them in a variable that I can use anywhere in my code.

(I am in the process of learning Node and JavaScript. This question probably has been asked before; its that I just do not know what context to put this in and this would really help me with developing an understanding of JavaScript.)

var fs = require('fs');

function sql_file(sql_file, cb) {
    var fileName = "./SQLs/" + sql_file;
    fs.readFile(fileName, function(err, buffer) {
        if (err) return cb(err);
        return cb(null, buffer.toString());
    });
}


var t = sql_file('inventory.sql', function(err, contents) {
    return contents.toString();
});


console.log(t);

Here is where I am getting lost: if t is representative of the return of sql_file, then how come you get undefined?

KatieK
  • 12,546
  • 17
  • 71
  • 84
justZito
  • 559
  • 1
  • 9
  • 18

1 Answers1

1

As people have noted in the comments, the problem with your code is that you are missing the concept of asynchronous execution. If you add a few console.log() statements to your code to see when each step is executed you will be able to spot the problem:

var fs = require('fs');

function sql_file(sql_file, cb) {
    console.log('about to read file');
    var fileName = "./SQLs/" + sql_file;
    fs.readFile(fileName, function(err, buffer) {
        console.log('the file has been read');
        if (err) return cb(err);
        return cb(null, buffer.toString());
    });
    console.log('call to read file made (but not finished)');
    // the call to sql_file ends here and returns nothing/undefined
}


var t = sql_file('inventory.sql', function(err, contents) {
    // contents will have the value when this call back is executed
    // in your case this is once the file has been read. 
    // Keep in mind that sql_file will ALWAYS return null, though.
    return contents.toString();
});

console.log(t);

If you run your code with these console.log() you'll see why you are always getting undefined. Once your code uses an async call (like fs.readFile) you cannot depend on linear execution of the code.

You should write your code as follow in order for you to print the value as you intended:

var fs = require('fs');

function sql_file(sql_file, cb) {
    console.log('about to read file');
    var fileName = "./SQLs/" + sql_file;
    fs.readFile(fileName, function(err, buffer) {
        console.log('the file has been read');
        if (err) return cb(err);
        return cb(null, buffer.toString());
    });
    console.log('call to read file made (but not finished)');
}


// sql_file will always return NULL but the value will 
// be available to you once the callback is executed.    
sql_file('inventory.sql', function(err, contents) {

    console.log(contents.toString());
});

The take away here is that once you've gone async you are all in. You cannot expect the value to be available in the next line when one of the calls (fs.readFile in your case) is asynch.

Hector Correa
  • 24,933
  • 7
  • 55
  • 69
  • Wow Hector, I can actual understand what is happening here, it sux because all I knew for the past 10yrs has been VB. Some VB concepts apply but VERY little. I think the async is where what I am having an issue with getting my head around. – justZito Mar 07 '14 at 16:31
  • Yeah, the async concept takes a little while to make sense and it is totally different from VB (or C# or Java or Ruby.) But once it does is not as hard as people make it sound. – Hector Correa Mar 07 '14 at 16:33
  • Hector, this is why I ask how can I put the contents of the file into a string. I have an oracle get with "var reader = connection.reader("SELECT * FROM Inventory", []);" I want a write once and run anywhere so var reader = connection.reader(sql_file('Inventory.sql'), []);". I am not looking for someone to do my homework here just to develop a better understanding. – justZito Mar 07 '14 at 16:43
  • If the connection.reader is async (and most likely it will be) you cannot just put it in a string and expect to use it in the next line. You need to assign the string inside the callback (like inside the callback for sql_file in your example). You'll need to move some of your code inside the callback, which is why so people talk about the "pyramid of doom" since everything starts to get nested. As you work on this approach the libraries and ideas that people mentioned in the comments will start to make more sense. But you might need to feel the pain so to speak first and then try them out. – Hector Correa Mar 07 '14 at 18:39
  • 1
    Hector I got it working just the way I needed it too, you are are real asset to this forum... – justZito Mar 07 '14 at 19:03