O'Reilly logo

Learning Node, 2nd Edition by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Interactive Node with REPL and More on the Console

While you’re exploring the use of Node and figuring out the code for your custom module or Node application, you don’t have to type JavaScript into a file and run it with Node to test your code. Node also comes with an interactive component known as REPL, or read-eval-print loop.

REPL (pronounced “repple”) supports simplified line editing and a small set of basic commands. Whatever you type into REPL is, for the most part, processed no differently than if you had typed the JavaScript into a file and run the file using Node. You can actually use REPL to code your entire application—literally testing the application on the fly.

In this chapter, I’ll cover how to use REPL, as well as some interesting quirks of REPL and how to work with them. These workarounds include replacing the underlying mechanism that persists commands, as well as using some command-line editing. And if the built-in REPL doesn’t provide exactly what you need for an interactive environment, there’s also an API to create your own custom REPL.

REPL is an essential Node development tool, and so is the console. We’re using the console in most applications in the book, but there’s more to this helpful object than just logging messages.

REPL: First Looks and Undefined Expressions

To begin REPL, simply type node without providing any Node application file, like so:

$ node

REPL then provides a command-line prompt—an angle bracket (>)—by default. Anything you type from this point on is processed by the underlying V8 JavaScript engine.

REPL is very simple to use. Just start typing in your JavaScript, like you’d add it to a file:

> a = 2;
2

The tool prints out the result of whatever expression you just typed. In this session excerpt, the value of the expression is 2. In the following, the expression result is an array with three elements:

> b = ['a','b','c'];
[ 'a', 'b', 'c' ]

To access the last expression, use the underscore/underline special variable (_). In the following, a is set to 2, and the resulting expression is incremented by 1, and then 1 again:

> a = 2;
2
> ++_;
3
> ++_;
4

You can even access properties or call methods on the underscored expression:

> ['apple','orange','lime']
[ 'apple', 'orange', 'lime' ]
> _.length
3
> 3 + 4
7
> _.toString();
'7'

You can use the var keyword with REPL in order to access an expression or value at a later time, but you might get an unexpected result. For instance, consider the following line in REPL:

var a = 2;

It doesn’t return the value 2; it returns a value of undefined. The reason is that the result of the expression is undefined since variable assignment doesn’t return a result when evaluated.

Consider the following instead, which is what’s happening, more or less, under the hood in REPL:

console.log(eval('a = 2'));
console.log(eval('var a = 2'));

Typing the preceding line into a file and running that file using Node returns:

2
undefined

There is no result from the second call to eval; hence, the value returned is undefined. REPL is a read-eval-print loop, with emphasis on the eval.

You can use the variable in REPL, just as you would in a Node application:

> var a = 2;
undefined
> a++;
2
> a++;
3

The latter two command lines do have results, which are printed out by REPL.

Note

I’ll demonstrate how to create your own custom REPL—one that doesn’t output undefined—in “Custom REPL”.

To end the REPL session, either press Ctrl-C twice, or Ctrl-D once. We’ll cover other ways to end the session later, in “REPL Commands”.

Benefits of REPL: Getting a Closer Understanding of JavaScript Under the Hood

Here’s a typical demonstration of REPL:

> 3 > 2 > 1;
false

This code snippet is a good example of how REPL can be useful. At first glance, we might expect the expression just typed to evaluate to true, since 3 is greater than 2, which is greater than 1. However, in JavaScript, relational operators are evaluated left to right, and each expression’s result is returned for the next evaluation.

A better way of looking at what’s happening with the preceding code snippet is this REPL session:

> 3 > 2 > 1;
false
> 3 > 2;
true
> true > 1;
false

Now the result makes more sense. What’s happening is that the expression 3 > 2 is evaluated, returning true. But then the value of true is compared to the numeric 1. JavaScript provides automatic data type conversion, after which true and 1 are equivalent values. Hence, true is not greater than 1, and the result is false.

REPL’s helpfulness is in enabling us to discover these little interesting quirks in JavaScript and our code. Hopefully, after testing our code in REPL, we don’t have unexpected side effects in our applications (such as expecting a result of true but getting a result of false).

Multiline and More Complex JavaScript

You can type the same JavaScript into REPL just like you’d type it into a file, including require statements to import modules. A session to try out the Query String (qs) module is repeated in the following text:

$ node
> qs = require('querystring');
{ unescapeBuffer: [Function],
  unescape: [Function],
  escape: [Function],
  encode: [Function],
  stringify: [Function],
  decode: [Function],
  parse: [Function] }
> val = qs.parse('file=main&file=secondary&test=one').file;
[ 'main', 'secondary' ]

Since we didn’t use the var keyword, the expression result is printed out—in this instance, it is the interface for the querystring object. How’s that for a bonus? Not only are you getting access to the object, but you’re also learning more about the object’s interface while you’re at it. However, if you want to forgo the potentially lengthy output of text, use the var keyword:

> var qs = require('querystring');

You’ll be able to access the querystring object with the qs variable with either approach.

In addition to being able to incorporate external modules, REPL gracefully handles multiline expressions, providing a textual indicator of code that’s nested following an opening curly brace ({):

> var test = function (x, y) {
... var val = x * y;
... return val;
... };
undefined
> test(3,4);
12

REPL displays repeating dots to indicate that everything that’s being typed follows an open curly brace and hence the command isn’t finished yet. It does the same for an open parenthesis, too:

> test(4,
... 5);
20

Increasing levels of nesting generates more dots; this is necessary in an interactive environment, where you might lose track of where you are as you type:

> var test = function (x, y) {
... var test2 = function (x, y) {
..... return x * y;
..... }
... return test2(x,y);
... }
undefined
> test(3,4);
12
>

You can type in, or copy and paste in, an entire Node application and run it from REPL. I trimmed the actual display values for the Server, shown in bold, both because they’re so long, and because they’ll most likely change by the time you read this:

> var http = require('http');
undefined
> http.createServer(function (req, res) {
...
...   // content header
...   res.writeHead(200, {'Content-Type': 'text/plain'});
...
...   res.end("Hello person\n");
... }).listen(8124);
{ domain:
   { domain: null,
     _events: { error: [Function] },
     _maxListeners: undefined,
     members: [] },
  _
...
  httpAllowHalfOpen: false,
  timeout: 120000,
  _pendingResponseData: 0,
  _connectionKey: '6:null:8124' }
> console.log('Server running at http://127.0.0.1:8124/');
Server running at http://127.0.0.1:8124/
Undefined

You can access this application from a browser no differently than if you had typed the text into a file and run it using Node.

The “downside” of not assigning an expression to the variable is that you are going to get what could be a long object display in the midst of your application. However, one of my favorite uses of REPL is to get a quick look at objects. For instance, the Node core object global is sparsely documented at the Node.js website. To get a better look, I opened up a REPL session and passed the object to the console.log method like so:

> console.log(global)

I could have done the following, which has a similar result, with the addition of the gl variable (trimmed, for space):

> gl = global;
...
_: [Circular],
  gl: [Circular] }

Just printing out global also provides the same information:

> global

I’m not replicating what was displayed in REPL. I’ll leave that for you to try on your own installation, since the interface for global is so large. The important point to take away from this exercise is that we can, at any time, quickly and easily get a look at an object’s interface. It’s a handy way of remembering what a method is called or what properties are available.

Note

There’s more on global in Chapter 2.

You can use the up and down arrow keys to traverse through the commands you’ve typed into REPL. This can be a handy way of reviewing what you’ve done, as well as a way of editing what you’ve typed, though in a somewhat limited capacity.

Consider the following session in REPL:

> var myFruit = function(fruitArray,pickOne) {
... return fruitArray[pickOne - 1];
... }
undefined
> fruit = ['apples','oranges','limes','cherries'];
[ 'apples',  'oranges', 'limes',  'cherries' ]
> myFruit(fruit,2);
'oranges'
> myFruit(fruit,0);
undefined
> var myFruit = function(fruitArray,pickOne) {
... if (pickOne <= 0) return 'invalid number';
... return fruitArray[pickOne - 1];
... };
undefined
> myFruit(fruit,0);
'invalid number'
> myFruit(fruit,1);
'apples'

Though it’s not demonstrated in this printout, when I modified the function to check the input value, I actually arrowed up through the content to the beginning function declaration, and then hit Enter to restart the function. I added the new line, and then again used the arrow keys to repeat previously typed entries until the function was finished. I also used the up arrow key to repeat the function call that resulted in an undefined result.

It seems like a lot of work just to avoid retyping something so simple, but consider working with regular expressions, such as the following:

> var ssRe = /^\d{3}-\d{2}-\d{4}$/;
undefined
> ssRe.test('555-55-5555');
true
> var decRe = /^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/;
undefined
> decRe.test(56.5);
true

I’m absolutely useless when it comes to regular expressions, and have to tweak them several times before they’re just right. Using REPL to test regular expressions is very attractive. However, retyping long regular expressions would be a monstrous amount of work.

Luckily, all we have to do with REPL is arrow up to find the line where the regular expression was created, tweak it, hit Enter, and continue with the next test.

In addition to the arrow keys, you can also use the Tab key to autocomplete a command if there’s no confusion as to what you’re completing. As an example, type va at the command line and then press Tab; REPL lists out both var and valueOf, both of which can complete the typing. However, typing querys and hitting Tab returns the only available option, querystring. You can also use the Tab key to autocomplete any global or local variable. Table 4-1 offers a quick summary of keyboard commands that work with REPL.

Table 4-1. Keyboard control in REPL
Keyboard entry What it does

Ctrl-C

Terminates current command. Pressing Ctrl-C twice forces an exit.

Ctrl-D

Exits REPL.

Tab

Autocompletes global or local variable.

Up arrow

Traverses up through command history.

Down arrow

Traverses down through command history.

Underscore (_)

References result of last expression.

If you’re concerned about spending a lot of time coding in REPL with nothing to show for it when you’re done, no worries: you can save the results of the current context with the .save command. It and the other REPL commands are covered in the next section.

REPL Commands

REPL has a simple interface with a small set of useful commands. In the preceding section, I mentioned .save. The .save command saves your inputs in the current object context into a file. Unless you specifically created a new object context or used the .clear command, the context should comprise all of the input in the current REPL session:

> .save ./dir/session/save.js

Only your inputs are saved, as if you had typed them directly into a file using a text editor.

Here is the complete list of REPL commands and their purposes:

.break

If you get lost during a multiline entry, typing .break will start you over again. You’ll lose the multiline content, though.

.clear

Resets the context object and clears any multiline expression. This command basically starts you over again.

.exit

Exits REPL.

.help

Displays all available REPL commands.

.save

Saves the current REPL session to a file.

.load

Loads a file into the current session (.load /path/to/file.js).

If you’re working on an application using REPL as an editor, here’s a hint: save your work often using .save. Though current commands are persisted to history, trying to re-create your code from history would be a painful exercise.

Speaking of persistence and history, now let’s go over how to customize both with REPL.

REPL and rlwrap

The Node.js website documentation for REPL mentions setting up an environmental variable so you can use REPL with rlwrap. What is rlwrap, and why would you use it with REPL?

The rlwrap utility is a wrapper that adds GNU readline library functionality to command lines that allow increased flexibility with keyboard input. It intercepts keyboard input and provides additional functionality, such as enhanced line editing, as well as a persistent history of commands.

You’ll need to install rlwrap and readline to use this facility with REPL, and most flavors of Unix provide an easy package installation. For instance, in my own Ubuntu system, installing rlwrap was this simple:

apt-get install rlwrap

Mac users should use the appropriate installer for these applications. Windows users have to use a Unix environmental emulator, such as Cygwin.

Here’s a quick and visual demonstration of using REPL with rlwrap to change the REPL prompt to purple:

NODE_NO_READLINE=1 rlwrap -ppurple node

If I always want my REPL prompt to be purple, I can add an alias to my bashrc file:

alias node="NODE_NO_READLINE=1 rlwrap -ppurple node"

To change both the prompt and the color, I’d use the following:

NODE_NO_READLINE=1 rlwrap -ppurple -S "::> " node

Now my prompt would be:

::>

but in purple.

The especially useful component of rlwrap is its ability to persist history across REPL sessions. By default, we have access to command-line history only within a REPL session. By using rlwrap, the next time we access REPL, we’ll have access not only to a history of commands within the current session, but also to a history of commands in past sessions (and other command-line entries). In the following session output, the commands shown were not typed in, but were instead pulled from history with the up arrow key, after I had exited REPL and then re-entered it:

::> e = ['a','b']; 
[ 'a', 'b' ] 
::> 3 > 2 > 1; 
false

As helpful as rlwrap is, we still end up with undefined every time we type in an expression that doesn’t return a value. However, we can adjust this, and other functionality, just by creating our own custom REPL, discussed next.

Custom REPL

Node provides an API that we can use to create a custom REPL. To do so, first we need to include the REPL module (repl):

var repl = require("repl");

To create a new REPL, we call the start method on the repl object. The syntax for this method is:

repl.start(options);

The options object takes several values; the ones I want to focus on are:

prompt

Default is >.

input

Readable stream; default is process.stdin.

output

writable stream; default is process.stdout.

eval

Default is an async wrapper for eval.

useGlobal

Default is false to start a new context rather than use the global object.

useColors

Whether writer function should use colors. Defaults to REPL’s terminal value.

ignoreUndefined

Default is false: don’t ignore the undefined responses.

terminal

Set to true if stream should be treated like a tty (terminal), including support for ANSI/VT100 escape codes.

writer

Function to evaluate each command, and return formatting. Defaults to util.inspect.

replMode

Whether REPL runs in strict mode, default, or hybrid.

Note

Starting in Node 5.8.0, repl.start() no longer requires an options object.

I find the undefined expression result in REPL to be unedifying, so I created my own REPL. I also redefined the prompt and set the mode to strict, which means each line executed is done so under “use strict.”

var repl = require('repl');

repl.start( {
  prompt: 'my repl> ',
  replMode: repl.REPL_MODE_STRICT,
  ignoreUndefined: true,
});

I ran the file, repl.js, using Node:

node repl

I can use the custom REPL just like I use the built-in version, except now I have a different prompt and no longer get the annoying undefined after the first variable assignment. I do still get the other responses that aren’t undefined:

my repl> let ct = 0;
my repl> ct++;
0
my repl> console.log(ct);
1
my repl> ++ct;
2
my repl> console.log(ct);
2

In my code, I wanted the defaults used for all but the listed properties. Not listing any other property in the options object causes each to use the default.

You can replace the eval function with your custom REPL. The only requirement is that it has a specific format:

function eval(cmd, callback) {
   callback(null, result);
}

The input and output options are interesting. You can run multiple versions of REPL, taking input from both the standard input (the default), as well as sockets. The documentation for REPL at the Node.js site provides an example of a REPL listening in on a TCP socket, using the following code:

var net = require("net"),
    repl = require("repl");

connections = 0;

repl.start({
  prompt: "node via stdin> ",
  input: process.stdin,
  output: process.stdout
});

net.createServer(function (socket) {
  connections += 1;
  repl.start({
    prompt: "node via Unix socket> ",
    input: socket,
    output: socket
  }).on('exit', function() {
    socket.end();
  })
}).listen("/tmp/node-repl-sock");

net.createServer(function (socket) {
  connections += 1;
  repl.start({
    prompt: "node via TCP socket> ",
    input: socket,
    output: socket
  }).on('exit', function() {
    socket.end();
  });
}).listen(5001);

When you run the application, you get the standard input prompt where the Node application is running. However, you can also access REPL via TCP. I used PuTTY as a Telnet client to access this TCP-enabled version of REPL. It does work…to a point. I had to issue a .clear first, the formatting is off, and when I tried to use the underscore to reference the last expression, Node didn’t know what I was talking about. I also tried with the Windows Telnet client, and the response was even worse. However, using my Linux Telnet client worked without a hitch.

The problem here, as you might expect, is Telnet client settings. However, I didn’t pursue it further, because running REPL from an exposed Telnet socket is not something I plan to implement, and not something I would recommend, either—at least, not without heavy security. It’s like using eval() in your client-side code, and not scrubbing the text your users send you to run—but worse.

You could keep a running REPL and communicate via a Unix socket with something like the GNU Netcat utility:

nc -U /tmp/node-repl-sock

You can type in commands no differently than typing them in using stdin. Be aware, though, if you’re using either a TCP or Unix socket, that any console.log commands are printed out to the server console, not to the client:

console.log(someVariable); // actually printed out to server

An application option that I consider to be more useful is to create a REPL application that preloads modules. In the application in Example 4-1, after the REPL is started, the third-party modules of Request (powerful HTTP client), Underscore (utility library), and Q (promise management) modules are loaded and assigned to context properties.

Example 4-1. Creating a custom REPL that preloads modules
var repl = require('repl');
var context = repl.start({prompt: '>> ',
                          ignoreUndefined: true,
                          replMode: repl.REPL_MODE_STRICT}).context;

// preload in modules
context.request = require('request');
context.underscore = require('underscore');
context.q = require('q');

Running the application with Node brings up the REPL prompt, where we can then access the modules:

>> request('http://blipdebit.com/phoenix5a.png')
.pipe(fs.createWriteStream('bird.png'))

The core Node modules don’t need to be specifically included; just access them by their module name directly.

If you want to run the REPL application like an executable in Linux, add the following line as the first line in the application:

#!/usr/local/bin/node

Modify the file to be an executable and run it:

$ chmod u+x replcontext.js
$ ./replcontext.js
>>

Stuff Happens—Save Often

Node’s REPL is a handy interactive tool that can make our development tasks a little easier. REPL allows us not only to try out JavaScript before including it in our files, but also to actually create our applications interactively and then save the results when we’re finished.

Another useful REPL feature is that it enables us to create a custom REPL so that we can eliminate the unhelpful undefined responses, preload modules, change the prompt or the eval routine we use, and more.

I also strongly recommend that you look into using REPL with rlwrap in order to persist commands across sessions. This could end up being a major time saver. Plus, who among us doesn’t like additional editing capability?

As you explore REPL further, there’s one very important thing to keep in mind from this chapter: stuff happens. Save often.

If you’ll be spending a lot of time developing in REPL, even with the use of rlwrap to persist history, you’re going to want to frequently save your work. Working in REPL is no different than working in other editing environments, so I’ll repeat: stuff happens—save often.

The Necessity of the Console

There are few examples in this book that don’t make use of the console. The console is a way of printing out values, checking operations, verifying the asynchronous nature of an application, and providing some kind of feedback.

For the most part, we’re using console.log() and just printing out messages. But there’s more to the console than a server version of the alert box in the browser.

Console Message Types, Console Class, and Blocking

In most of the examples in the book, we’re using console.log() because we’re only interested in feedback while we’re experimenting with Node. This function outputs the message to stdout, typically the terminal. When you start putting together Node applications for a production environment, though, you’re going to want to make use of other console messaging functions.

The console.info() function is equivalent to console.log(). Both write to stdout; both output a newline character as part of the message. The console.error() function differs in that it outputs the message (again, with a newline character) to stderr, rather than stdout:

console.error("An error occurred...");

The console.warn() function does the same.

Both types of messaging appear in the terminal, so you might wonder what the difference is. In actuality, there really is no difference. To understand that, we need to look more closely at the console object.

Using a Logging Module

You’re not limited to the built-in console object for logging. There are more sophisticated tools available, such as the Bunyan and Winston modules.

First of all, the console object is a global object instantiated from the Console class. We can, if we wish, actually create our own version of the console using this same class. And we can do so in two different ways.

To create a new instance of Console, we either need to import the Console class or access it via the global console object. Both of the following result in a new console-like object:

// using require
var Console = require('console').Console;

var cons = new Console(process.stdout, process.stderr);
cons.log('testing');

// using existing console object
var cons2 = new console.Console(process.stdout, process.stderr);

cons2.error('test');

Notice how in both instances, the process.stdout and process.stderr properties are passed as writable stream instances for writing log messages and error messages, respectively. The console global object is created in just this manner.

I covered process.stdout and process.stderror in Chapter 2. What we know about both is that they map to the stdout and stderr file descriptors in the environment, and that they’re different from most streams in Node in that they typically block—they’re synchronous. The only time they’re not synchronous is when the streams are directed at a pipe. So, for the most part, the console object blocks for both console.log() and console.error(). However, this isn’t an issue unless you’re directing a large amount of data at the stream.

So why use console.error() when an error occurs? Because in environments where the two streams are different, you want to ensure the proper behavior. If you’re in an environment where log messages don’t block but an error does, you want to ensure a Node error does block. In addition, when you run a Node application, you can direct the output for console.log() and console.error() to different files using command-line redirection. The following directs the console.log() messages to a logfile, and the errors to an error file:

node app.js 1> app.log 2> error.log

The following Node application:

// log messages
console.log('this is informative');
console.info('this is more information');

// error messages
console.error('this is an error');
console.warn('but this is only a warning');

directs the first two lines to app.log, and the second two to error.log.

To return to the Console class, you can duplicate the functionality of the global console object by using the Console class and passing in process.stdout and process.stderr. You can also create a new console-like object that directs the output to different streams, such as log and error files. The Console documentation provided by the Node Foundation provides just such an example:

var output = fs.createWriteStream('./stdout.log');
var errorOutput = fs.createWriteStream('./stderr.log');
// custom simple logger
var logger = new Console(output, errorOutput);
// use it like console
var count = 5;
logger.log('count: %d', count);
// in stdout.log: count 5

The advantage to using this type of object is that you can use the global console for general feedback, reserving the newly created object for more formal reporting.

Process and Streams

As noted, the process object is covered in Chapter 2, and streams are covered in Chapter 6.

Formatting the Message, with Help from util.format() and util.inspect()

All four console functions, log(), warn(), error(), and info(), can take any data type, including an object. Non-object values that aren’t a string are coerced to a string. If the data type is an object, be aware that Node only prints out two levels of nesting. If you want more, you should use JSON.stringify() on the object, which then prints out a more readable indented tree:

var test = {
   a : {
       b : {
          c : {
            d : 'test'
          }
       }
   }
}

// only two levels of nesting are printed out
console.log(test);

// three levels of nesting are printed
var str = JSON.stringify(test, null, 3);

console.log(str);

The output of the application is:

{ a: { b: { c: [Object] } } }
{
   "a": {
      "b": {
         "c": {
            "d": "test"
         }
      }
   }
}

If you use a string, you can use printf-like formatting with the string for all four functions:

var val = 10.5;
var str = 'a string';

console.log('The value is %d and the string is %s', val, str);

This approach is advantageous if you’re working with data passed as function arguments or collected from a web request. The type of formatting allowed is based on the formatting supported for the util.format() utilities module, which you could also use directly to create the string:

var util = require('util');

var val = 10.5,
    str = 'a string';

var msg = util.format('The value is %d and the string is %s',val,str);
console.log(msg);

Though if you’re only using the one function, it’s simpler to just use the formatting in console.log(). Allowable format values are:

%s
string
%d
number (both integer and float)
%j
JSON. Replaced with ['circular'] if the argument contains circular references
%%
to use a literal percentage sign (%)

Extra arguments are converted to strings and concatenated to the output. If there are too few arguments, the placeholder itself is printed out:

var val = 3;

// results in 'val is 3 and str is %s'
console.log('val is %d and str is %s', val);

The four functions covered are not the only ones used to provide feedback. There’s also console.dir().

The console.dir() function differs from the other feedback functions in that whatever object is passed to it is passed, in turn, to util.inspect(). This Utilities module function provides more finite control over how an object is displayed via a secondary options object. Like util.format(), it can also be used directly. An example is:

var test = {
   a : {
       b : {
          c : {
            d : 'test'
          }
       }
   }
}

var str = require('util').inspect(test, {showHidden: true, depth: 4 });
console.log(str);

The object is inspected and the result is returned as a string based on what is passed in the options object. Options are:

  • showHidden: to display non-enumerable or symbolic properties (default is false)
  • depth:  how many times to recurse to inspect object (default is 2)
  • colors: if true, output is styled with ANSI color codes (default is false)
  • customInspect: if false, then custom inspect functions defined on the objects being inspected won’t be called (default is true)

The color mapping can be defined globally using the util.inspect.styles object. You can modify the global colors, too. Use console.log() to print out the object properties:

var util = require('util');

console.log(util.inspect.styles);
console.log(util.inspect.colors);

The application in Example 4-2 modifies the object being printed to add a date, a number, and a boolean. In addition, the color value for the boolean is changed from yellow to blue, to differentiate it from the number (by default, they’re both yellow). The object is printed out using various methods: after processing with util.inspect(), using console.dir() with same options, using the basic console.log() function, and using the JSON.stringify() function on the object.

Example 4-2. Different formatting options for printing out an object
var util = require('util');

var today = new Date();

var test = {
   a : {
       b : {
          c : {
            d : 'test'
          },
          c2 : 3.50
       },
       b2 : true
   },
   a2: today
}

util.inspect.styles.boolean = 'blue';

// output with util.inspect direct formatting
var str = util.inspect(test, {depth: 4, colors: true });
console.log(str);

// output using console.dir and options
console.dir(test, {depth: 4, colors: true});

// output using basic console.log
console.log(test);

// and JSON stringify
console.log(JSON.stringify(test, null, 4));

The result is shown in Figure 4-1 so you can see the color effects. I use a white background for my terminal window, with black text.

Figure 4-1. Capture of terminal window showing different formats for string output
Note

The console.dir() function supports three of the four util.inspect() options: showHidden, depth, and colors. It doesn’t support customInspect. If set to true, this option indicates that the object is actually supplying its own inspection function.

Providing Richer Feedback with console and a Timer

Returning to the console object, one additional approach that can provide a more in-depth picture of what’s happening with an application is to add a timer and output a begin and end time.

The two console functions we’re using for this functionality are console.time() and console.timeEnd(), passing a timer name to both.

In the following code snippet, the code is using a longer-lasting loop so that enough time passes for the timer functions to register a time difference.

console.time('the-loop');

for (var i = 0; i < 10000; i++) {
   ;
}

console.timeEnd('the-loop');

Even with a largish loop, the time involved will barely register. How long depends on the load in the machine, as well as the process. But the timer functionality isn’t limited to synchronous events. Being able to use a specific name with the timer means we can use the functionality with asynchronous events.

Here, I modify the Hello World application from Chapter 1, adding in a start to the timer at the beginning of the application, ending it for each web request, and then re-starting it. It times the gaps in time between each request. Sure, we could use a Date() function to do a more representative timer, but what’s the fun of that?

var http = require('http');

console.time('hello-timer');
http.createServer(function (request, response) {
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Hello World\n');
  console.timeEnd('hello-timer');
  console.time('hello-timer');
}).listen(8124);

console.log('Server running at http://127.0.0.1:8124/');

In all seriousness, what the code demonstrates is that you can incorporate the timer into asynchronous operations, thanks to the ability to individually name the timers.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required