I'm new to NodeJS, and I'm attracted to the performance benefits of asynchronously and concurrently performing IO operations so that the total request time is closer to the time required to perform the slowest IO operation rather than the sum of all IO operations.
However all the callbacks are a bit messy and I want to do my best to make my code as DRY as possible.
I have a simple 'brochure' type 5 page website that loads header + body + footer views for any normal page request, but loads only the body for an AJAX request (with a get param 'ajaxify') -- the resulting content is injected into the body of the page on the client.
I'm looking for some tips on how to manage my code design when dealing with concurrent/asynchronous IO operations and form a response when all IO operations have completed.
This is my first try with a NodeJS module (written in coffeescript -- I've supplied the compiled javascript as well)
Is there any type of design pattern for this type of problem that is commonly used?
os = require 'os'
url = require 'url'
html_minifier = require 'html-minifier'
render_views = (req, res, body_view, code = 200) ->
complete = (arg) ->
response = arg.response
code = arg.code
if query.hasOwnProperty 'ajaxify'
res.send html_minifier.minify response.body,
collapseWhitespace: true
removeComments: true,
code
else
if response.header != '' and response.body != '' and response.footer != ''
res.send html_minifier.minify response.header + response.body + response.footer,
collapseWhitespace: true
removeComments: true,
code
query = url.parse(req.url, true).query
response =
header: ''
body: ''
footer: ''
if query.hasOwnProperty 'ajaxify'
code = 200
res.render body_view, {}, (err, html) ->
response.body = html
complete
response: response
code: code
else
res.render body_view, {}, (err, html) ->
response.body = html
complete
response: response
code: code
res.render 'view_header', {}, (err, html) ->
response.header = html
complete
response: response
code: code
res.render 'view_footer', {}, (err, html) ->
response.footer = html
complete
response: response
code: code
exports.index = (req, res) ->
render_views(req, res, 'view_index')
exports.services = (req, res) ->
render_views(req, res, 'view_services')
exports.about = (req, res) ->
render_views(req, res, 'view_about')
exports.blog = (req, res) ->
render_views(req, res, 'view_blog')
exports.contact = (req, res) ->
render_views(req, res, 'view_contact')
exports.error = (req, res) ->
render_views(req, res, 'view_error', '404')
And regular compiled javascript:
// Generated by CoffeeScript 1.4.0
(function() {
var html_minifier, os, render_views, url;
os = require('os');
url = require('url');
html_minifier = require('html-minifier');
render_views = function(req, res, body_view, code) {
var complete, query, response;
if (code == null) {
code = 200;
}
complete = function(arg) {
var response;
response = arg.response;
code = arg.code;
if (query.hasOwnProperty('ajaxify')) {
return res.send(html_minifier.minify(response.body, {
collapseWhitespace: true,
removeComments: true
}, code));
} else {
if (response.header !== '' && response.body !== '' && response.footer !== '') {
return res.send(html_minifier.minify(response.header + response.body + response.footer, {
collapseWhitespace: true,
removeComments: true
}, code));
}
}
};
query = url.parse(req.url, true).query;
response = {
header: '',
body: '',
footer: ''
};
if (query.hasOwnProperty('ajaxify')) {
code = 200;
return res.render(body_view, {}, function(err, html) {
response.body = html;
return complete({
response: response,
code: code
});
});
} else {
res.render(body_view, {}, function(err, html) {
response.body = html;
return complete({
response: response,
code: code
});
});
res.render('view_header', {}, function(err, html) {
response.header = html;
return complete({
response: response,
code: code
});
});
return res.render('view_footer', {}, function(err, html) {
response.footer = html;
return complete({
response: response,
code: code
});
});
}
};
exports.index = function(req, res) {
return render_views(req, res, 'view_index');
};
exports.services = function(req, res) {
return render_views(req, res, 'view_services');
};
exports.about = function(req, res) {
return render_views(req, res, 'view_about');
};
exports.blog = function(req, res) {
return render_views(req, res, 'view_blog');
};
exports.contact = function(req, res) {
return render_views(req, res, 'view_contact');
};
exports.error = function(req, res) {
return render_views(req, res, 'view_error', '404');
};
}).call(this);
You can simplify your code a little bit (assuming you are using express) by using req.xhr (or by checking for the X-Requested-With header). This way you can drop all the query string stuff...
Replace
query.hasOwnProperty('ajaxify')
with
req.xhr
Alternatively you could move the logic straight to the view if you like... just put if(!req.xhr) equivalent around header/footer and it will all be done there. All your render_views method would do in that case is call res.render and do the html minification (Making it much simpler).