10 new io.js features you should be using in your Node.js app

Datetime:2016-08-23 00:06:45          Topic: io.js           Share

The io.js project has grown up incredibly fast, invigorating the Node ecosystem. In its short existence, io.js has brought a host of bug fixes, performance enhancements, and new features.

If you haven’t kept up with the activity, don’t fear. This article aims to summarize the highlights to get you up to speed. We will tackle features introduced from oldest to newest with examples and relevant documentation where applicable. Following each feature description will be the version it was first introduced in . All the code samples will use stable ES6 features available in io.js. Speaking of that…

ES6 features (1.0.0)

One of the goals for io.js has been to walk closely with recent updates to V8 and namely, to take advantage of ES6 features. Generators are one such (long-awaited) feature on by default as well as a host of ES6 functionality considered stable enough to run without flags. Rather than regurgitate ES6 support here, check out the documentation below for details and documentation on each feature.

Supported ES6 Features

Note: Node 0.12 also has some ES6 features enabled by default and more under the --harmony flag.  The behavior/availability of ES6 features differs from io.js though (e.g. generators are not on by default in Node).

Improved error documentation (1.2.0)

Ever get confused about what EPIPE , or EMFILE, or ENOENT mean? I’ve been there. Thankfully, io.js has dramatically improved the error documentation with a dedicated errors page:

Errors Documentation

Simpler stream construction (1.2.0)

Formerly, when implementing a stream, you would extend the base stream and implement one or more methods depending on the type of stream. For example, a Transform stream expects a _transform method and optionally a _flush method. Below is an example of a Transform stream splitting a file into lines:

var stream = require ( 'stream' )

var liner = new stream . Transform ( { objectMode : true } )

liner . _transform = function ( chunk , enc , done ) {

var data = chunk . toString ( )

if ( this . _lastLineData ) {

data = this . _lastLineData + data

}

var lines = data . split ( '\n' )

this . _lastLineData =

lines . splice ( lines . length - 1 , 1 ) [ 0 ]

lines . forEach ( this . push . bind ( this ) )

done ( )

}

liner . _flush = function ( done ) {

if ( this . _lastLineData ) {

this . push ( this . _lastLineData )

}

this . _lastLineData = null

done ( )

}

module . exports = liner

This kinda looks nasty with all the method guts hanging out. Now you can pass those methods as options to the constructor. Just remove the leading underscore ( _ ). The liner transform rewritten now looks like this (using object literal extensions in ES6):

'use strict'

const stream = require ( 'stream' )

let liner = new stream . Transform ( {

// Include any existing constructor options

objectMode : true ,

// This is the _transform method

transform ( chunk , enc , done ) {

let data = chunk . toString ( )

if ( this . _lastLineData ) {

data = this . _lastLineData + data

}

let lines = data . split ( '\n' )

this . _lastLineData =

lines . splice ( lines . length - 1 , 1 ) [ 0 ]

lines . forEach ( this . push . bind ( this ) )

done ( )

} ,

// This is the _flush method

flush ( done ) {

if ( this . _lastLineData ) {

this . push ( this . _lastLineData )

}

this . _lastLineData = null

done ( )

}

} )

module . exports = liner

Stream Constructor Documentation

Look up all IP addresses for a domain (1.2.0)

When doing a dns.lookup prior, you only got back the first result. Now there is an { all: true } option to get back an array of all results.

'use strict'

const dns = require ( 'dns' )

// Returns first address

dns . lookup ( 'google.com' , console . log )

// => '173.194.46.40' 4

// Returns all resolved addresses in an array

dns . lookup ( 'google.com' , { all : true } , console . log )

/* => [ { address: '173.194.46.40', family: 4 },

{ address: '173.194.46.38', family: 4 },

...

{ address: '2607:f8b0:4009:804::1007', family: 6 } ]

*/

dns.lookup Documentation

The unhandledRejection event (1.4.1)

If a promise gets rejected, but no-one is there to see it, did the rejection ever happen? Why yes, it did, but no-one will never know about it! As you can guess, this is a source of hard-to-find errors. Take a look at this simple example:

'use strict'

let delay = function ( ms ) {

return new Promise ( function ( resolve ) {

setTimeout ( resolve , ms )

} )

}

delay ( 2000 )

. then ( function ( data ) {

data . foo = 'hello'

} )

Can you spot the error? The delay function returns a promise that will resolve in some amount of time. When the promise resolves, the result will be undefined . So we end up with a ReferenceError because we tried to access the property foo on an undefined value. Yet, we will never know that happened and will be scratching our heads because unhandled rejections just sit in limbo. Although some promise libraries warn you in this case, ES6 promises will not. Thankfully you can check for these pesky beasts using an unhandledRejection event on the process global.

process . on ( 'unhandledRejection' , function ( er ) {

console . log ( 'got unhandled rejection' , er . stack )

} )

If you are using ES6 promises, its highly recommended you have one of these set up so rejections don’t go unnoticed.

process.on(“unhandledRejection”) Documentation

Note: There is also a “rejectionHandled” event emitted when handling a promise rejection on a later turn in the event loop. Use this to nullify false positives caught in the “unhandleRejection” event. For a detailed explanation of why this exists see the docs .

StreamWrap and JSStream (1.4.1)

A bridge between C++ and JS streams now exists such that you can use a normal Duplex stream as input when interfacing with lower level C++ streams like sockets. The tls.connect() method takes advantage of this. Take a look at the test suite for an example.

Buffer#indexOf method (1.5.0)

Here is a handy method to search a buffer by a string, buffer, or number. This method behaves like Array#indexOf in that it returns the index of the start position of the first match it finds in the buffer. You can also pass a start index optionally as a second parameter.

'use strict'

const assert = require ( 'assert' )

let buf = new Buffer ( 'abc def ghi' )

assert . equal ( buf . indexOf ( 'abc' ) , 0 )

assert . equal ( buf . indexOf ( 'bc' ) , 1 )

assert . equal ( buf . indexOf ( 'def' ) , 4 )

assert . equal ( buf . indexOf ( 'c' ) , 2 )

assert . equal ( buf . indexOf ( 'c' , 4 ) , 11 )

A complementary lastIndexOf method is in discussion.

buffer#indexOf Documentation

Preload modules (1.6.0)

You can now preload modules when executing scripts or using the REPL by using the -r or --require flag. For example:

iojs - r . / foo - r bar my - app . js

Is synonymous with:

require ( './foo' )

require ( 'bar' )

require ( './my-app' )

This enables some interesting use cases. The first is injecting functionality into an existing application that is useful in certain cases. For example, you want to instrument an application with the ability to take heap snapshots to debug memory leaks. Instead of maintaining a configuration like:

if ( cfg . useHeapdump ) require ( 'heapdump' )

You could just start the app with that module preloaded when you want that functionality:

iojs - r heapdump app . js

A second application is compilers (Babel, CoffeeScript, etc.). For example, If you use Babel to compile ES6 or ES7 code, your entry point may contain something like this:

require ( 'babel/register' )

require ( './my-actual-app' )

Now, you can set that up from the command line without the wrapper:

iojs - r babel / register my - actual - app . js

Babel in this instance already has a babel-node tool that does just that and more, but hopefully you get the point.

Debugging synchronous I/O (2.1.0)

Synchronous I/O is convenient, especially for shell scripting but in many applications it hinders performance (like servers). You could search for Sync in your code base but what if you are using a third-party module that doesn’t follow the naming scheme? This is where the --trace-sync-io flag comes in. Whenever a synchronous call executes, you get notified of the stack trace.

Let’s look at a simple example:

'use strict'

const http = require ( 'http' )

const cp = require ( 'child_process' )

http . createServer ( function ( req , res ) {

let stdout = cp . execFileSync ( 'whoami' )

res . end ( ` $ { stdout } \ n ` )

} ) . listen ( 3000 )

Here we have an HTTP server that executes synchronous code on every request. Not recommended. If we execute iojs --trace-sync-io server.js and then visit http://localhost:3000, we will see this warning in our console:

WARNING : Detected use of sync API

at spawnSync ( child_process . js : 1241 : 27 )

at execFileSync ( child_process . js : 1291 : 13 )

at / Users / wavded / Projects / whats - new - iojs / server . js : 6 : 19

at emitTwo ( events . js : 87 : 13 )

at emit ( events . js : 172 : 7 )

at parserOnIncoming ( _http_server . js : 474 : 12 )

at parserOnHeadersComplete ( _http_common . js : 88 : 23 )

at socketOnData ( _http_server . js : 325 : 22 )

at emitOne ( events . js : 77 : 13 )

Grab bag

Here are few other smaller features worth noting:

  1. You can require('./') also by using require('.') (1.6.2).
  2. When using console.log (and friends) or the underlying util.inspect , ES6 Promise , Map , and Set objects are pretty printed (2.0.0).
  3. The behavior for os.tmpdir() is now consistent across operating systems (2.0.0). Prior to this, some operating systems would have trailing slashes and some not leading to hard-to-find bugs. Now it will never have a trailing slash.
  4. Various optimizations from query string parsing (1.6.1), reduced tls memory usage (2.0.0), a faster process.nextTick (2.0.0), and a faster util.format for single arguments (2.1.0).
  5. io.js is enforcing best security practices. The Diffie-Hellman key exchange (DHE) parameters must be 1024 bits or longer (2.1.0).

How do I stay informed?

I find the best information in the CHANGELOG . Also io.js keeps an official blog up over at Medium which includes version updates as well as other information about the project.





About List