The 10 Key Features in Node.js v6 LTS Boron After You Upgrade
As we've previously covered in the Node.js v6.9.0 Release Brief, the Node.js v6 release line went into LTS this week. This is a major step for both Node.js and its users. The release brings a slew of feature additions to a Long Term Service release line.
With that, you may be wondering what some of the best features added to the newly minted v6 LTS are when compared to the Node.js v4 LTS release line. Luckily, we've compiled a list of 10 of the most useful and interesting new features below - including some highlights like the DevTools Inspector, unhandled Promise rejection warnings, and the Process Warnings API!
1. DevTools Inspector Integration
Last year, the Chromium team approached the Node core team and enquired whether there was any interest in re-using the DevTools debugger that was bundled with Blink as a way of interacting with Node.js. The Node.js debugger has not been well cared for over the years and even though itâs functional, JavaScript debuggers in modern browsers have advanced well beyond what Node can offer natively.
In Node.js v6.3.0, Googleâs v8_inspector protocol was extracted from Blink and shipped with Node. This functionality is still considered âexperimentalâ by the Node core team, meaning it is not yet extensively documented and may still be removed in a future version of Node without going through a deprecation cycle. However, given the popularity and power of this tool, and the this is unlikely to happen. A more likely outcome would be for the old debugger to eventually be removed and completely replaced by this new feature.
When Node.js is run with the --inspect
command-line argument (with optional port number argument), a chrome-devtools://
URL is printed to the console. Entering this URL in a Chrome web browser will launch a remote debugging connection directly into the process. Add the additional --debug-brk
command line argument to break on the first line of your application so you have time to work with the debugger. You can use Chromeâs DevTools to debug a Node application with similar power with which you can debug frontend JavaScript, including features such as live code editing and full asynchronous call stacks. Read Paul Irishâs post for more details on the kinds of features available right now in Node.js v6 LTS.
Source: Paul Irish's article, Debugging Node.js with Chrome DevTools
Far from being exclusive to Chrome, this new protocol is a WebSockets JSON protocol that is well documented and is already implemented in a number of clients and servers. Already the Visual Studio Code editor announced support for this experimental feature and you can even use it from command line interfaces.
2. Capture Names of Listeners on an EventEmitter
The eventNames()
method - added in Node.js 6.0.0 - will return an array with the names of all the events that are being listened to by user callbacks on a given EventEmitter
object. This kind of functionality wasnât previously available unless you were using the internal _events
property.
Finding the names of events being listened to can be useful for detecting when an event isnât being listened toâwhich allows for attaching listeners to handle events that arenât being handled, or to ignore work thatâs specific to some events that may not be needed otherwise.
3. Overhauled Buffers Constructor APIs
The Buffer Constructor API was overhauled, deprecating the old new Buffer(...)
and adding Buffer.from()
and Buffer.alloc()
as the preferred replacements. The APIs were added to Node core in v5.10.0, and allow two different uses: Buffer.from()
creates a Buffer from an Array-like (such as an Array, String, or another Buffer), and Buffer.alloc()
creates a zero-filled Buffer of a specified size.
Additionally, a the --zero-fill-buffers
CLI flag was added in v5.10.0 to automatically force zero-filling of all newly created Buffers in a Node application.
The new Buffer constructor feature set provides a clearer interface in order to make it less likely that an application does not accidentally leak old memory space by improper Buffer creation.
Use of the plain Buffer()
constructor is deprecated in the Node.js documentation which explains why the new API should be used. In a future version of Node.js, a warning will be printed to standard error when a Buffer is created using the old constructor as well.
4. Unhandled Promise
rejection warnings
One of the criticisms often made against Promises is the ease with which errors can be swallowed and ignored. Since io.js and Node.js v5, the âunhandledRejectionâ
and ârejectionHandledâ
events, emitted on the process
object, have been available to provide some insight into Promise rejections that are not handled. Due to the semantics of Promise error handling, itâs not as clear as an uncaughtException
since a rejection could potentially be stored and handled at a later time. In fact, one of the early candidates for the âunhandledRejectionâ
event name was âpossiblyUnhandledRejectionâ
. Modern idiomatic Promise usage, however, suggests that this is an anti-pattern and that rejection handlers should be placed on a promise near to its creation, either directly on construction or straight afterward.
Since Node.js v6.6.0, the âunhandledRejectionâ
event now also causes a warning to be printed to standard error.
$ node
> new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Whoa!')) }, 100) })
Promise { <pending> }
> (node:35449) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Whoa!
This behavior can be turned off (at your peril!) with the --no-warnings
command line argument or made more verbose to include stack traces with the --trace-warnings
command line argument so you can track down the location of the errant code.
$ node
$ node --trace-warnings
> new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Whoa!')) }, 100) })
Promise { <pending> }
> (node:35484) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Whoa!
at emitPendingUnhandledRejections (internal/process/promises.js:57:27)
at runMicrotasksCallback (internal/process/next_tick.js:61:9)
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickDomainCallback (internal/process/next_tick.js:122:9)
Since a rejection handler could still be handled after Promise creation, via the catch()
method, the warning is not emitted or printed until after the next tick of the event loop after the rejection.
$ node
> function resolver (resolve, reject) { setTimeout(() => { reject(new Error('Whoa!')) }, 100) }
undefined
> // rejection handler attached on same tick:
> p = new Promise(resolver); p.catch((err) => { console.error(err) });
Promise { <pending> }
> Error: Whoa!
at Timeout.setTimeout (repl:1:81)
at ontimeout (timers.js:365:14)
at tryOnTimeout (timers.js:237:5)
at Timer.listOnTimeout (timers.js:207:5)
> // rejection handler added on a later tick, causing an additional ârejectionHandledâ event
> p = new Promise(resolver)
Promise { <pending> }
> (node:35560) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Whoa!
> p.catch((err) => { console.error(err) });
Promise { <pending> }
> (node:35560) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
Error: Whoa!
at Timeout.setTimeout (repl:1:81)
at ontimeout (timers.js:365:14)
at tryOnTimeout (timers.js:237:5)
at Timer.listOnTimeout (timers.js:207:5)
5. Quick and Safe Temporary Directory Creation
The fs.mkdtemp()
API was added to Node core in v5.10.0 to provide a guaranteed way to create a unique temporary directory. The API generates six random characters to be appended behind a required directory prefix
argument. This functionality was previously possible with user-created modules like unique-temp-dir
although JavaScript implementations of this functionality all suffer from performance problems compared to using native system calls and not all are guaranteed safe.
This API allows you to integrate with system default temporary directories, to absolutely ensure that there wonât be directory conflicts. Having this as a standardized feature in Node.js is a guarantee that the API will be consistent for any module or application that needs to use temporary directories.
6. Timing Attack Prevention
The crypto.timingSafeEqual()
API was added to Node core in v6.6.0 to help avoid timing attacks.
The API allows comparison without leaking timing information about the comparison, which could lead to a malicious party being able to infer values being compared. With the addition of this API to the crypto
module allows it to be used outside of assert
. As a rule of thumb, use this API if you need to compare values, where one is derived from user-input and another is a secret (or derived from a secret).
7. Process Warnings API
The new (Process Warning)(https://nodejs.org/dist/latest-v6.x/docs/api/process.html#process_event_warning) API was added in v6.0.0 and adds the ability to listen to process warnings emitted by Node.js, and provides an API to override the default handler, to be used in custom logging system for instance. For example, if you are using a custom JSON logger you can now catch Node core warnings and also have them logged as JSON.
The API can also be used by non-core code to emit non-fatal warnings as appropriate. e.g. process.emitWarning('Something Happened!', 'CustomWarning');
or process.emitWarning('This API is deprecated', 'DeprecationWarning');
.
Currently, Node core emits:
âDeprecationWarningâ
s when âruntime deprecatedâ core APIs are used.âPromiseRejectionHandledWarningâ
when aPromise
is rejected but there is not rejection handler attached to receive it.âMaxListenersExceededWarningâ
when anEventListener
has more listeners added than themaxListeners
count (10 by default). This is normally an indication of a possible memory leak, where listeners are added but not properly removed when no longer needed.
Warnings are still printed to standard error, but so are custom userland errors:
$ node -e 'process.emitWarning("Something Happened!", "CustomWarning");'
(node:33526) CustomWarning: Something Happened!
Note that the warning output also now contains the process ID.
In addition, this API also brings some new command line arguments to adjust warning output:
--no-warnings
to disable printing to standard error (the internalâwarningâ
event will still fire)--no-deprecation
to disable printing deprecation warnings (only) to standard error (the internalâwarningâ
event will still fire)--trace-warnings
to have the stack trace printed to standard error, mirroring error output, useful for finding where a deprecated API is being used from in your own code or a dependency--trace-deprecation
for stack traces just for deprecation warnings--throw-deprecation
to treat a deprecation warning as a thrown error
$ node -e 'require("sys")'
(node:33668) DeprecationWarning: sys is deprecated. Use util instead.
$ node --no-deprecation -e 'require("sys")'
$ node --trace-deprecation -e 'require("sys")'
(node:33681) DeprecationWarning: sys is deprecated. Use util instead.
at sys.js:10:6
at NativeModule.compile (bootstrap_node.js:497:7)
at Function.NativeModule.require (bootstrap_node.js:438:18)
at Function.Module._load (module.js:426:25)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at [eval]:1:1
at ContextifyScript.Script.runInThisContext (vm.js:25:33)
at Object.exports.runInThisContext (vm.js:77:17)
at Object.<anonymous> ([eval]-wrapper:6:22)
$ node --throw-deprecation -e 'require("sys")'
internal/process/warning.js:45
throw warning;
^
DeprecationWarning: sys is deprecated. Use util instead.
at sys.js:10:6
at NativeModule.compile (bootstrap_node.js:497:7)
at Function.NativeModule.require (bootstrap_node.js:438:18)
at Function.Module._load (module.js:426:25)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at [eval]:1:1
at ContextifyScript.Script.runInThisContext (vm.js:25:33)
at Object.exports.runInThisContext (vm.js:77:17)
at Object.<anonymous> ([eval]-wrapper:6:22)
8. Symlink Preservation
Instructs the module loader to preserve symbolic links when resolving and caching modules. When turned on, module paths are preserved when setting __dirname
and __filename
and also when using the location to resolve paths to other modules using require()
instead of using the ârealpathâ of the linked file.
The original discussion for this feature used the following example of behavior that doesnât work when symbolic links arenât preserved:
Resolves properly:
app
index.js //require("dep1")
node_modules
dep1
index.js //require("dep2")
dep2
index.js //console.log('fun!'):
Does not resolve, where the user expectation may be that it should since the link is in an appropriate location.
app
index.js //require("dep1")
node_modules
dep1 -> ../../dep1
dep2
index.js
dep1
index.js //require("dep2")
This kind of behavior is sought when developing using peer dependencies that could be linked together rather than manually copied.
Preserving symbolic links was enabled as the default behavior in Node.js v6.0.0, under a mistaken belief that the impact to the ecosystem would be mostly positive. Unfortunately a number of use-cases were discovered via bug reports where the new functionality was breaking applications or causing poor performance because of assumptions made based on how Node previously worked. You can read more about the problem in the original thread which became very active after v6.0.0 was launched and was the focal point of discussion regarding how to approach the problems being reported.
The --preserve-symlinks
command line argument was finally added and the default behavior restored in v6.2.0. While the core team have suggested that --preserve-symlinks
is only a temporary solution until a better one can be found, so far there have not been proposals for a way forward.
9. V8 Performance Profiling Directly Through Node.js
The new --prof-process
command line argument, added in Node.js v5.2.0, runs the built-in formatter for V8 performance profiles. These profiles have been possible with Node.js for some time by using the --prof
command line argument that is passed straight to V8.
When an application is run with --prof
, a file with a name such as isolate-0x102004c00-v8.log
(where the octal number changes with each run) is created for each âisolateâ (an isolated V8 VM instance with its own heap) within the application (child processes or use of the vm
module may cause an application to use more than a single isolate).
Unfortunately, these profiling log output files are mostly not human-readable and are very verbose:
$ node --prof ./map-bench.js
$ wc isolate-0x*.log
3375 4571 419868 isolate-0x102004c00-v8.log
3801 4968 514577 isolate-0x102801000-v8.log
4319 5493 628883 isolate-0x103800a00-v8.log
11495 15032 1563328 total
V8 also ships with what it calls a âtick processorâ that is able to parse these files and produce human-readable and useful output. Formerly you would have to install this from npm, such as tick, but the problem for authors and users is that the tick processor needs to be kept up to date with each version of V8 in order to produce helpful output from the log files it produces. Shipping a tick processor within Node.js removes this need and also make a helpful code profiling tool easily accessible for users.
$ node --prof-process isolate-0x103800a00-v8.log
Statistical profiling result from isolate-0x103800a00-v8.log, (2819 ticks, 201 unaccounted, 0 excluded).
[Shared libraries]:
ticks total nonlib name
17 0.6% /usr/lib/system/libsystem_platform.dylib
6 0.2% /usr/lib/system/libsystem_c.dylib
[JavaScript]:
ticks total nonlib name
112 4.0% 4.0% Stub: StringAddStub_CheckNone_NotTenured
47 1.7% 1.7% Stub: CEntryStub
43 1.5% 1.5% LazyCompile: *runFakeMap /home/rvagg/node/benchmark/es/map-bench.js:49:20
âŚ
[C++]:
ticks total nonlib name
276 9.8% 9.9% v8::internal::NameDictionaryBase<v8::internal::NameDictionary, v8::internal::NameDictionaryShape>::FindEntry(v8::internal::Handle<v8::internal::Name>)
239 8.5% 8.5% v8::internal::StringTable::LookupKey(v8::internal::Isolate*, v8::internal::HashTableKey*)
127 4.5% 4.5% v8::internal::HashTable<v8::internal::NameDictionary, v8::internal::NameDictionaryShape, v8::internal::Handle<v8::internal::Name> >::Rehash(v8::internal::Handle<v8::int
âŚ
[Summary]:
ticks total nonlib name
372 13.2% 13.3% JavaScript
2223 78.9% 79.5% C++
43 1.5% 1.5% GC
23 0.8% Shared libraries
201 7.1% Unaccounted
âŚ
(Output heavily trimmed to show only some highlights)
Not only can you use the output to see which parts of your code are taking up most of the VMs time and where they are being called from but you also get some insight into how the VM is treating your code. For instance, the *
next to JavaScript function names indicates that the code was optimized by V8. More information on how to read this data can be found on the V8 wiki.
Of course, if you would like live profiling of applications while they run in production, you should try NodeSourceâs N|Solid to view similar data in graphical format.
10. Process CPU usage
The process.cpuUsage()
API was added to Node core in v6.1.0, and returns an object that contains both user and system CPU time of the current process in microseconds.
The API allows inference of how much time that the CPU was active by checking the total time difference in between two calls of the API, with some additional legwork.