Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing parallel asynchronicity using q.all of q.js in a node.js application

Tags:

node.js

q

The background briefing: I have written the code below to instruct node.js to: (1) assemble an HTML page from three text files in which I stored the pageheader, pagebody and pagefooter components, respectively. Obviously, these page fragments must be assembled in the proper order. On the other hand, there is no requirement at all that these page fragments must be extracted from their text files in any order prior to their being assembled. And I want to take advantage of that by implementing parallel asynchronicity.

The implementation code:

var sys = require('sys'),
http = require('http'),
fs = require('fs'),
Q = require('q'),
fullpage, pageheader, pagebody, pagefooter;

fullpage = '';

fs_readheader = fs.readFile('./htmlfiles.1', 'utf-8', function (err, data) {
   if (err) { throw err; }
   return pageheader = data;
});

fs_readbody = fs.readFile('./htmlfiles.2', 'utf-8', function (err, data) {
  if (err) { throw err; }
  return pagebody = data;
});

fs_readfooter = fs.readFile('./htmlfiles.3', 'utf-8', function (err, data) {
  if (err) { throw err; }
  return pagefooter = data;
});

finish = function(err, data) {
  if (err) { throw err; }
  console.log(pageheader);
  data = pageheader + pagebody + pagefooter;
  console.log(data);
  return fullpage = data;
}();


Q.all([fs_readheader,fs_readbody,fs_readfooter]).then(finish);


http.createServer(function(request, response) {
  response.writeHeader(200, {"Content-Type": "text/html"});
  response.write(fullpage);
  response.end();
}).listen(8001);

The Issue: the node.js server is not displaying anything at 127.0.0.1:8001 I expected, of course, a full rendering of the HTML page. I am using version 1.00 of q.js, which is the latest version of q.js at this point in time.

Aftermath: In response to Barry-Johnson's comprehensive input, I have modified Barry-Johnson's code to this form:

var http = require('http'),
  fs = require('fs'),
  Q = require('q');

var readFileP = Q.denodeify(fs.readFile);

http.createServer(function(request, response) {
  Q.all([ readFileP('./htmlfiles.1', 'utf-8'),
          readFileP('./htmlfiles.2', 'utf-8'),
          readFileP('./htmlfiles.3', 'utf-8') ])
    .done(function(content){
        response.writeHead(200, {"Content-Type": "text/html"});
        response.end(content.join(''));
    }); 

}).listen(8001);

The result is exactly what I want, a fully assembled HTML page - in the correct order. I added '' as a parameter to the "join" method because I don't want the pieces to be joined by a comma. I used the "done" method because I wanted any uncaught error to float up. I understand that, in general, we don't want any production code to crash and Barry-Johnson's implementation of the "fail" method is fine for that purpose.

like image 884
Vietnhi Phuvan Avatar asked Feb 13 '23 15:02

Vietnhi Phuvan


1 Answers

SLaks is correct regarding promises. And more generally, you either had a bad copy/paste or the promises issue is sort of secondary to so much else you have going on. Getting your promises working in the above code would not alone get the result I believe you are seeking.

However focusing on the promises - you need to look at Q.denodeify, Q.ninvoke, Q.nfapply etc. See the Adapting Node section of the Q doc. These are calls that let you work with functions which follow the standard node convention of having a trailing callback and instead manipulate them as promise-returning functions.

Here's the good news - you've got a lot less code you need to deal with. A version with some comments the leaves in some of the original verbosity:

var sys = require('sys'),
    http = require('http'),
    fs = require('fs'),
    Q = require('q'),
    fullpage, pageheader, pagebody, pagefooter;

var readFileP = Q.denodeify(fs.readFile);

var fsReadheader = readFileP('./file1.html', 'utf-8');
var fsReadBody = readFileP('./file2.html', 'utf-8');
var fsReadFooter = readFileP('./file3.html', 'utf-8');

//  This is where you had Q.all before - which is probably not what you want,
// or did you want to just build the page once and have it static for the life
// of the app? If that is the case, promises are kind of pointless and you may
// as well K.I.S.S. and just read the bits synchronously at startup

http.createServer(function(request, response) {
    // you should probably wait to write out the response until you know if you have success.
    // but again, maybe you do want this to be totally static content
    response.writeHead(200, {"Content-Type": "text/html"});
    // I am assuming you wanted to load the files at request time, possibly
    // in preparation to adapting your code to make ti more dynamic.
    Q.all([fsReadheader,fsReadBody,fsReadFooter])
        .then(function (x) {
            // this was your old 'finish' function - doesn't really do much.
            // I used join instead of concatenating. less typing. And no typing
            // if you ever add file4
            return x.join();})
        .then(function(content){
            // You could response.write() as well, but you can send data with .end()
            response.end(content);
        })
        .fail(function(e){
            // here is where your error handler goes. any error will end up here.'
            // you could have more granular erro handling
            response.writeHead(500, 'Bad news, Tex');
            response.end('No dice');
        });
    //
}).listen(8001);

However, unless I was getting paid by the keystroke, I would write it like this:

var http = require('http'),
    fs = require('fs'),
    Q = require('q');

var readFileP = Q.denodeify(fs.readFile);

http.createServer(function(request, response) {
    Q.all([ readFileP('./file1.html', 'utf-8'),
            readFileP('./file2.html', 'utf-8'),
            readFileP('./file3.html', 'utf-8') ])
        .then(function(content){
            response.writeHead(200, {"Content-Type": "text/html"});
            response.end(content.join());
        })
        .fail(function(e){
            response.writeHead(500, 'Bad news, Tex');
            response.end('No dice');
        });
    //
}).listen(8001);
like image 101
barry-johnson Avatar answered Feb 16 '23 05:02

barry-johnson