44

I'm trying to get my head around synchronous versus asynchronous in Node.js, in particular for reading an HTML file.

In a request handler, the synchronous version that I'm using, which works is the following:

    var fs = require("fs");
    var filename = "./index.html";
    var buf = fs.readFileSync(filename, "utf8");
    
    function start(resp) {
        resp.writeHead(200, { "Content-type": "text/html" });
        resp.write(buf);
        resp.end();
    }
    
    exports.start = start; 
  1. What would be the version using readFile()?
  2. I understand that readFile is asynchronous so theoretically, I should wait for the entire file to be read before rendering it, so should I introduce an addListener? I might be confusing different things.

Edit: I have tried to refactor the code like this:

    var fs = require("fs");
    var filename = "./index.html";
    function start (resp) {
        resp.writeHead(200, { "Content-Type": "text/html" });
        fs.readFile(filename, "utf8", function (err, data) {
            if (err) throw err;
            resp.write(data);
        });
        resp.end();
    }

I get a blank page. I guess it's because it should wait for all the data to be read, before resp.write(data), how do I signal this?

Max
  • 1,054
  • 1
  • 12
  • 20
Bondifrench
  • 1,272
  • 1
  • 20
  • 35
  • 1
    Are you asking, what would the equivalent code be if you were to use, `readFile()` and how would you handle sending the contents asynchronously? – Anthony Forloney Apr 04 '14 at 12:57
  • 1
    @AnthonyForloney Yes, that's what I'm asking, what is the equivalent code using fs.readFile() ? – Bondifrench Apr 04 '14 at 13:04
  • 2
    move the `resp.end()` within your callback. That is why you get a blank page. – Anthony Forloney Apr 04 '14 at 13:14
  • 1
    @AnthonyForloney Yes, i saw that with lombausch answer, do you know why I need to put resp.end() inside the callback? Thx – Bondifrench Apr 04 '14 at 13:19
  • 1
    When using asynchronous methods, such as `readFile` in your above code you are telling the program to, "read the contents of a file, and when you are done (keyword, 'when') reading the contents, use them as the response body". Without providing the `resp.end()` call inside of your callback (as seen above), the program invokes `readFile` and then immediately calls `resp.end()` before your callback has the time to perform the `resp.write`. Does that make sense? – Anthony Forloney Apr 04 '14 at 13:26
  • @Bondifrench Here is a description of what is the logic is work behind scenes of `readFileSync()` https://stackoverflow.com/a/56110224/4701635 – Paresh Barad May 13 '19 at 11:41

2 Answers2

43
var fs = require("fs");
var filename = "./index.html";

function start(resp) {
    resp.writeHead(200, {
        "Content-Type": "text/html"
    });
    fs.readFile(filename, "utf8", function(err, data) {
        if (err) throw err;
        resp.write(data);
        resp.end();
    });
}
Ionică Bizău
  • 109,027
  • 88
  • 289
  • 474
webduvet
  • 4,220
  • 2
  • 28
  • 39
  • 2
    Thanks, it does work. Follow up question, you may have seen my edit, in my tentative answer I didn't include resp.end() in the callback, which didn't work, would you know why it has to be inside the callback? Rgds – Bondifrench Apr 04 '14 at 13:16
  • 8
    If you do not provide it within the callback, your `resp.end()` will be immediately invoked as it is not waiting for the response from the callback. – Anthony Forloney Apr 04 '14 at 13:17
8

This variant is better because you could not know whether file exists or not. You should send correct header when you know for certain that you can read contents of your file. Also, if you have branches of code that does not finish with '.end()', browser will wait until it get them. In other words, your browser will wait a long time.

var fs = require("fs");
var filename = "./index.html";

function start(resp) {

    fs.readFile(filename, "utf8", function(err, data) {
        if (err) {
            // may be filename does not exists?
            resp.writeHead(404, {
                'Content-Type' : 'text/html'
            });
            // log this error into browser
            resp.write(err.toString());
            resp.end();
        } else {

            resp.writeHead(200, {
                "Content-Type": "text/html"
            });      
            resp.write(data.toString());
            resp.end();
        }
    });
}
Jed Fox
  • 2,979
  • 5
  • 28
  • 38