Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at the New York ColdFusion User Group (Jan. 2009) with: Simon Free
Ben Nadel at the New York ColdFusion User Group (Jan. 2009) with: Simon Free@simonfree )

Function Hoisting, Prototype Chains, Exports, And process.nextTick() In Node.js

By Ben Nadel on

In my demo yesterday, on consuming a Crypto Hash object as a Stream in Node.js, I ran my demo inside of a process.nextTick() callback. I did this in order allow my demo code to be fully defined before I consumed it. Sometimes you need this; sometimes you don't. It depends on what values are being hoisted and how those values are being used. Since this is often a point of confusion for people new to JavaScript, I thought it would be worth a quick exploration in Node.js.


 
 
 

 
 
 
 
 

First, let's take a look at a demo that won't work. In the following code, I'm trying to consume a Writable Stream sub-class that is defined later on in the same file:

  • // Require our core node modules.
  • var stream = require( "stream" );
  • var util = require( "util" );
  •  
  •  
  • // ----------------------------------------------------------------------------------- //
  • // ----------------------------------------------------------------------------------- //
  •  
  •  
  • // Create an instance of our writable stream and try to write to it.
  • var myStream = new SomeStream();
  •  
  • // CAUTION: This will ERROR because at this point in time, we were able to call
  • // the constructor (SomeStream) due to function-hoisting; but, the .inherits() and
  • // .prototype lines of code have not had a chance to run. As such, SomeStream has not
  • // had a chance to actually inherit from stream.Writable at this point.
  • myStream.write( "Hello world!" );
  • myStream.write( "What it be like?!" );
  •  
  •  
  • // ----------------------------------------------------------------------------------- //
  • // ----------------------------------------------------------------------------------- //
  •  
  •  
  • // I am a sub-class of Writable stream.
  • function SomeStream() {
  •  
  • // Call the super constructor.
  • stream.Writable.call( this );
  •  
  • }
  •  
  • util.inherits( SomeStream, stream.Writable );
  •  
  •  
  • // When you write to this stream, all I do is turn around and log it to the console.
  • SomeStream.prototype._write = function( chunk, encoding, doneWriting ) {
  •  
  • console.log( "Written to stream:", chunk.toString( "utf-8" ) );
  •  
  • doneWriting();
  •  
  • };

As you can see, we try to instantiate the SomeStream() class and then write to it. When we run this code, we get the following console error output:

Bens-iMac:process-nexttick ben$ node plain.js
/Users/ben/Sites/bennadel.com/testing/nodejs/process-nexttick/plain.js:18
myStream.write( "Hello world!" );
.........^
TypeError: undefined is not a function

It's important to understand where this is failing. The error is not in the instantiation of the SomeStream() class - in fact, the instantiation worked. The error occurs when we try to call the .write() method on the resultant object. The .write() method is part of the Writable stream prototype. But, our SomeStream() class didn't actually have a chance to extend the Writable stream.

The reason for this is that, while the Function declaration for SomeStream() was hoisted to the top of the code, the .inherits() and the .prototype references were not. So, while the SomeStream() class is technically available, it was only partially defined at the time I attempted to consume it:


 
 
 

 
 Function hoisting in JavaScript and how it affects node.js code. 
 
 
 

To get around this, I could have placed my demo code at the bottom of the file so that it would execute after the SomeStream() class had been fully defined. That would have worked perfectly. But, sometimes, I like to have my demo code at the top of the file so that it's the first thing you see. To do that, I can place my demo code inside of a process.nextTick() callback:

  • // Require our core node modules.
  • var stream = require( "stream" );
  • var util = require( "util" );
  •  
  •  
  • // ----------------------------------------------------------------------------------- //
  • // ----------------------------------------------------------------------------------- //
  •  
  •  
  • // Wait until the end of this event loop to run the demo code. This will give the
  • // current file a chance to define all of the variables including the prototypes. We
  • // always get the variables and function declarations hoisted; but, this next-tick
  • // will allow the .inherits() and .prototype lies to be executed pre-demo.
  • process.nextTick(
  • function runDemo() {
  •  
  • // Create an instance of our writable stream and try to write to it.
  • var myStream = new SomeStream();
  •  
  • myStream.write( "Hello world!" );
  • myStream.write( "What it be like?!" );
  •  
  • }
  • );
  •  
  •  
  • // ----------------------------------------------------------------------------------- //
  • // ----------------------------------------------------------------------------------- //
  •  
  •  
  • // I am a sub-class of Writable stream.
  • function SomeStream() {
  •  
  • // Call the super constructor.
  • stream.Writable.call( this );
  •  
  • }
  •  
  • util.inherits( SomeStream, stream.Writable );
  •  
  •  
  • // When you write to this stream, all I do is turn around and log it to the console.
  • SomeStream.prototype._write = function( chunk, encoding, doneWriting ) {
  •  
  • console.log( "Written to stream:", chunk.toString( "utf-8" ) );
  •  
  • doneWriting();
  •  
  • };

When I do this, the entire file executes before my demo code actually "runs." process.nextTick() is kind of like the scope.$evalAsync() method in AngularJS - it doesn't actually delay the execution in terms of event loop iterations; it really just moves the execution to end of the current event loop cycle.

So, if my process.nextTick() callback gets executed at the end of the current event loop cycle, it will execute after my SomeStream() class has been defined, including the call to .inherits() and the .prototype assignments.

NOTE: I am not saying you should write demo code this way - this is more a matter of personal style than anything else.

Now, in a real Node.js application, this rarely matters because the call to require() is synchronous. This means that when you .require() a JavaScript module, the entire file executes before the require() call returns. As such, you never have to worry about the timing of the calling context and you can safely export a class Constructor at the top of the file:


 
 
 

 
 The require() method in node.js is a synchronous call that evaluates the node module before exposing exported values. 
 
 
 

Unless you're writing a Node.js demo, you probably won't need process.nextTick() in your main execution path. But, I think it's important to understand how Function and Variable hoisting work and what problem process.nextTick() is solving in this scenario. Of course, process.nextTick() has other purposes, such as ensuring asynchronous execution. But, that's beyond the scope of this post.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.