In 2015, the ECMAScript committee released a major update of the JavaScript language. The browser makers are adding those much-needed features, meaning the V8 engine is adding those features as well. These features are making their way into Node.js starting with version 4.x.
To learn about the current status of ES-2015 in Node.js, visit https://nodejs.org/en/docs/es6/.
By default, only the ES-2015 features which V8 considers stable are enabled. Further features can be enabled with command-line options. The almost complete features are enabled with the --es_staging
option. The features still in progress are enabled by the --harmony_destructuring
option. The website documentation gives more information.
The ES-2015 features make a big improvement in the JavaScript language. One feature, the Promise
class, should mean a fundamental rethinking of common idioms in Node.js programming. In ES-2016, a pair of new keywords, async
and await
, will simplify coding asynchronous code in Node.js, and it should encourage the Node.js community to further rethink the common idioms of the platform.
There's a long list of new JavaScript features, but let's quickly go over two of them we'll use extensively.
The first is lighter weight function syntax called the Arrow Function:
fs.readFile('file.txt', 'utf8', (err, data) => { if (err) …; // do something with the error else …; // do something with the data });
This is more than the syntactic sugar of replacing the function
keyword with the fat arrow. The value of this
is the same as it was outside this invocation. This means that, when using an arrow function, we don't have to jump through hoops to bring this
into the callback function because this
is the same at both levels of the code.
The next feature is the Promise
class, which is used for deferred and asynchronous computations. Deferred code execution to implement asynchronous behavior is a key paradigm for Node.js, and it requires two idiomatic conventions:
While convenient, these conventions resulted in multilayer code pyramids that can be difficult to understand and maintain:
doThis(arg1, arg2, (err, result1, result2) => { if (err) …; else { // do some work doThat(arg2, arg3, (err2, results) => { if (err2) …; else { doSomethingElse(arg5, err => { if (err) .. ; else ..; }); } }); } });
Depending on how many steps are required for a specific task, a code pyramid can get quite deep. Promises will let us unravel the code pyramid and improve reliability because error handling is more straightforward and easily captures all errors.
A Promise
class is created as follows:
function doThis(arg1, arg2) { return new Promise((resolve, reject) => { // execute some asynchronous code if (errorIsDetected) return reject(errorObject); // When the process is finished call this: resolve(result1, result2); }); }
Rather than passing in a callback function, the caller receives a Promise
object. When properly utilized the above pyramid can be coded as follows:
doThis(arg1, arg2) .then((result1, result2) => { return doThat(arg2, arg3); }) .then((results) => { return doSomethingElse(arg5); }) .then(() => { // do a final something }) .catch(err => { // errors land here });
This works because the Promise
class supports chaining if a then
function returns a Promise
object.
At the time of writing, the Node.js community has barely begun to use this new paradigm, and instead it is still using the old pass in a callback function paradigm. In the interim, we'll have to be flexible enough to use both paradigms. Over the long term, there should be a switch to this paradigm.
The Babel transpiler (http://babeljs.io/) is a great way to use cutting edge JavaScript features on older implementations. The word transpile means Babel rewrites JavaScript code into other JavaScript code, specifically to rewrite ES-2015 or ES-2016 features to older JavaScript code. Babel does this by converting JavaScript source to an abstract syntax tree, then manipulating that tree to rewrite the code using older JavaScript features, and then writing that tree to a JavaScript source code file.
Since Node.js doesn't yet support all the ES-2015 features, we can use Babel to go ahead and use those features.
We're not ready to show example code for these features, but we can go ahead and document the setup of the Babel tool. For further information on setup documentation, visit http://babeljs.io/docs/setup/.
In the root directory of your project, type these commands:
$ npm install --save-dev babel-cli $ npm install babel-preset-es2015 –save-dev
This installs the Babel software, saving references in the package.json
file. We'll go over that file in more depth in the next chapter, but NPM uses package.json
to document attributes of the module. Some of those attributes are dependencies on other modules, and in this case, we're documenting a development dependency on Babel.
Because we installed babel-cli
, a babel
command is installed such that we can type the following:
$ ./node_modules/.bin/babel –help
Next, in the same directory as package.json
, create a file named .babelrc
containing the following:
{ "presets": ["es2015"] }
This instructs Babel to use presets
to configure the set of transformations it will perform.
The last step is to create a directory named src
to store code written with ES2015 or ES2016 features. The transpiled code will land in the directory named lib
and it's that code which will be executed.
To transpile your code, run the following command:
$ ./node_modules/.bin/babel src -d lib
This command asks to take all files in the src
directory and transpile each to a matching file name in the lib
directory.
To automate the process, edit the package.json
file and insert the following snippet:
"scripts": { "build": "babel src -d lib" },
With this, you can use the following command:
$ npm run build
We'll go over npm
scripts in more depth later, but this is a way to record certain commands for later use.