Enough Node for Building a Basic Command Line Script
In the last post we covered the very basics of what Node is and how to install it but now it’s time to write something that’s potentially useful. though most would jump directly into building a website, I want to do a little on the command line to explain a few concepts about node and in the process point you in the direction of how to make some useful tools for yourself.
Reading a File, Sync versus Async
As was mentioned in the previous post, a Node process is single-threaded and hands I/O-heavy tasks to other processes so that it can process further requests. In other words, instead of handling all I/O requests synchronously, the best way to generally use Node is to handle requests asynchronously. Though this is usually the behavior you want, you can break this rule. When creating a website this will almost always be a bad idea but when creating a command-line tool, it’s fine to force everything to be synchronous.
Let’s see this in action. Have you ever had to do some processing on a text file? Maybe you wrote some ad-hoc code to parse through a CSV file or had to convert some plain text to HTML. It’s something most programmers have to do everyone once in a while, so let’s create a simple script to do that. Let’s say we have a text file like the following.
Name: Bob Smith
Title: Chief Excel Master
Notes: Drinks way too much Pepsi but has good taste in steaks.
We want to turn this into HTML, with each line a paragraph and the label text on the left of each line in bold. Here is a script for doing that.
var fs = require('fs'); //#1
var contents = fs.readFileSync('textfile.txt', { encoding: 'utf8' }); //#2
var lines = contents.split('\n'); //#3
var html = '';
lines.forEach(function(line) {
var segments = line.split(':');
html += '<p><b>'
+ segments[0]
+ '</b>'
+ segments[1]
+ '</p>\n'
});
console.log(html);
- #1 - The first line contains a require statement, which brings in the 'fs' (FileSystem) module, which contains Node’s built-in code for interacting with the file system. Custom modules are referenced similarly, which is something we will discuss later.
- #2 - This second line of code actually reads the file. The method named “
readFileSync
” takes a path (in our case just the filename since the two files are in the same directory) and an encoding. The return value is the contents of the file. - #3 - The rest of the file is regular JavaScript, not related to Node at all so I won’t comment on it.
<p><b>Name</b> Bob Smith</p>
<p><b>Title</b> Chief Excel Master</p>
<p><b>Notes</b> Drinks way too much Pepsi but has good taste in steaks.</p>
Now let’s go back and discuss readFileSync
. If you look at the documentation
for the fs
module on the Node website, you’ll notice that most of the methods come in
pairs. In the case of readFileSync
, it has a corresponding method without the word
“Sync” on the end that takes the same arguments plus a callback. If you look at the
documentation for the FileSystem module of Node you will
probably notice that almost every method on the module comes in a pairs of async and sync methods
(e.g. open
and openSync
, mkdir
and mkdirSync
).
Many other languages like C# (I have spent most of my professional career writing in this language) and their
supporting frameworks take a very different
approach. C# has always supported multi-threading (so there has always been support
for writing synchronous and asynchronous code) and the async/await features of C# that were recently
added are quite fantastic. But despite that, synchronous is still the default. As an example if
you look at
the C# WebClient class
you will notice there there are a number of methods that come in
synchronous/asynchronous pairs but the naming pattern is different than what you find in Node. In C#,
the synchronous name is the default where the asynchronous name is the exception (e.g. DownloadFile
and DownloadFileAsync
, OpenRead
and OpenReadAsync
).
I spent that time talking about C# to make a point, which is that in Node asynchronous is the default mindset . So let’s take our read file example and re-implement it using the asynchronous version of the readFile API so we can see how we would more often use Node APIs.
var fs = require('fs');
var callback = function(err, contents) {
var lines = contents.split('\n');
var html = '';
lines.forEach(function(line) {
var segments = line.split(':');
html += '<p><b>'
+ segments[0]
+ '</b>'
+ segments[1]
+ '</p>\n'
});
console.log(html);
}
fs.readFile('textfile.txt', { encoding: 'utf8' }, callback);
There are only two important changes here. First is the change of the method name from
readFileSync
to readFile
. The second is that all the processing of
the text file was moved into a callback function that executes when the contents of the
file have been read from disk and are available for use. Note that readFile
doesn’t return the contents of the file. The reason why is because the file process
has been delegated elsewhere and node will immediately continue processing. But when the
file read operation is finished, the callback will be fired and the data will be passed
as the second parameter of the callback.
What We Covered and What’s Next
In this post we covered a few basic but important things.
- You learned that you can create scripts that are runnable from the command line. This means you can use Node to create useful utilities or process files.
- You learned that Node is asynchronous by default. Though there is certainly a time for synchronous operations, even its naming patterns are trying to enforce the centrality of asynchronicity in the framework.
Next we jump into creating a website.
comments powered by Disqus