5 Best New JavaScript Features

by Dan Cohn

javascript graphic

So, JavaScript, eh?

You may be wondering, why talk about JavaScript in the context of a serious travel services platform like Sabre Dev Studio.  Isn’t JavaScript merely a tool for building interactive web pages?  Does it really have the chops for mission-critical transaction processing workflows?

You may be surprised to learn that major outfits like Netflix, Paypal, Walmart, Uber, and even NASA rely on JavaScript, and Node.js in particular, to power their server-side applications.  JavaScript is the top programming language used by those who responded to Stack Overview’s 2018 developer survey, making it the most commonly-used language for six years in a row.  It’s also the most popular language on GitHub (by opened pull request), beating out number two Python by more than 2x.

JavaScript, more formally known as ECMAScript, has matured greatly in the past five years.  It has all the capabilities you’d expect from a modern language – functional programming, object-oriented programming, event-driven programming, garbage collection, reflection, closures, lambdas, exception handling, real-time debugging, and more.  Throw TypeScript over it and you’ve got strong and weak typing.  Best of all, with npm (the Node.js package manager), you gain access to a wealth of world-class frameworks, packages, and tools to build upon.  There are over 780,000 modules available, and the list grows by an astounding 566 per day (as of February 28, 2019).

Calling REST APIs is a snap with Express, Restify, or one of a variety of other HTTP client builders.  JSON is, of course, native to JavaScript, making it extremely easy to work with.  It’s even possible to consume SOAP/XML web services with Node, but here I will caution you to tread carefully as the existing packages (e.g., easysoap, node-soap) may have limitations in how complex a WSDL they can handle.  Java still leads the pack when it comes to SOAP and XML tools and support.

All this is well and good, but let me tell you what I love most about JavaScript:  There’s nothing to compile.  Make a change to your code and test it instantly.  Use a tool like nodemon or forever, and your application restarts automatically each time you save changes.

While I’m singing JavaScript’s praises, let me add to the list the fact that the language specification continues to evolve with thoughtful new additions each year.  The latest version is ECMAScript® 2020, also known as ES10, as it’s the tenth edition of the language.  A draft of the spec can be found here.

So, what about those new features?  Here are some new and relatively-new features I find interesting and useful:

1. Template Literals

These have been around since 2015, but they’re worth mentioning because they’re so handy and make your code more readable.  Two examples:

var message = `Hi, my name is ${first} ${last}.`;
console.log(`Received: ${JSON.stringify(obj)}`);

Inserting variables into a string is a common task, whether to construct a message header, assemble an object attribute, or produce a debug log.  With template literals, also known as string interpolation, you avoid the messy and error-prone prone process of closing and reopening quotes.  And you don’t have to remember the string concatenation operator.  (I sometimes forget if it’s a “+” like Java, a “.” like PHP, or an “&” as in Excel.  It’s sad, I know; most popular languages today use the + operator.)

Notice that you can include executable code with the curly braces.  This is particularly useful for calling functions (ideally without side effects) and performing calculations.

2. Array Includes

As of ES7, arrays provide an includes() function that returns true or false based on whether or not an item exists in the array.  Not a big deal, but this is cleaner than using the old ~array.indexOf() or array.indexOf() >= 0.

3. Asynchronous Functions

Let’s face it, callbacks are everywhere in JS applications, especially Node.  They’re a foundational part of any non-blocking library.  They work well but can lead to a situation known as “callback hell.”  This is where you land when you nest callback after callback until your code becomes so indented that you wish you used two spaces for indentation rather than four or (heaven forbid) eight.

Arguably the most important part of ES8 was the introduction of async and await.  It works in conjunction with Promises to flatten a chain of asynchronous calls.  Here’s a simple example:

1    var promise = new Promise((resolve, reject) => {
2        setTimeout(() => resolve('Done!'), 2000);
3    });
4    async function suspense() {
5        console.log('Wait for it...’);
6        var result = await promise;
7        console.log(result);
8    }
9    suspense();

Execution pauses on line 6 until the promise is resolved.  There’s no need to wrap any subsequent code (such as line 7) in a function block.  It runs normally once the callback returns.

Any function containing an “await” must be tagged with the “async” keyword to indicate that it may not complete within a single, unbroken thread of execution.

Bonus: A related new feature is Promise.all().  This gem invokes a set of promises in parallel and waits until they all resolve before execution continues.  The results are collected in an array.  Look it up to learn more.

I recommend checking out callbackhell.com for a longer, yet simple explanation of how all this works.

4. Array.flat and Array.flatMap

Coming soon to a JS interpreter near you are two new array prototype methods: flat and flatMap.  Calling flat() on an array flattens it by concatenating any nested arrays.  For example:

> ['a', ['b', 'c', 'd'], ['e'], [], 'f'].flat();
["a", "b", "c", "d", "e", "f"]

Believe it or not, there are situations in which this can be useful.  I leave it up to the reader to consider what they are (or Google it).  Note that flat() does not recursively flatten the array.  It works only one level deep.

Array.flatMap(), as the name implies, combines mapping and flattening in one step.  You pass it a function that maps each element of the array to another array.  The output is a single, flat array containing all of the resulting elements.  The advantage this has over the plain old map() function is that you can map each element to zero or more results.  For example:

function enumerate(n) {
    var result = new Array();
    for (let i = 0; i < n; ++i) result.push(n);
    return result;
}
> [4,3,2,1,0].flatMap(enumerate);
[4, 4, 4, 4, 3, 3, 3, 2, 2, 1]

Where this is perhaps most useful is when you want to filter and map at the same time.  You can accomplish this by returning an empty array for items you want to filter out, as with the last element (0) in the example above.  The regular map function always returns the same number of elements as there are in the input array.

5. Optional Catch Binding

Last and probably the least is this minor feature that allows you to create a catch block without declaring a variable in which to place the caught exception.  For example:

try {
    // some code that may throw an exception
} catch {
    …
}

Sometimes you just don’t care what exception was thrown or have no desire to do anything with it. This may not be a best practice, as normally you should at least log the exception, but that’s only true if the exception is unexpected. There are use cases involving predictable exceptions where omitting the catch binding can save you a few characters and potential lint warnings.  On the downside, it’s now easier to accidentally leave out a catch binding that ought to be there.

About Dan Cohn

Dan Cohn is a Principal Applications Architect for Sabre Labs, a technology incubator that explores early stage business concepts in travel to assess desirability, feasibility, and viability. He's been instrumental in building and piloting a chat-based virtual travel agent for corporate travelers. Dan has extensive background in software development, application and system design, and solution architecture. Before joining Sabre in early 2017, he spent 23 years in the telecommunications industry.