JavaScript is undergoing a massive evolution and increasingly taking on characteristics that make it attractive as a full stack environment.

The ES2017 (ES8) stack is already now being implemented in both Node and several modern browsers, addressing many of the more complex issues of web development including both better ways of dealing with asynchronous coding, ways of decorating content to make them better to work with tools such as React, and ways of dealing efficiently with word level manipulations used for vector processing.

In this article, I will be exploring a few of these new features, and will use them as springboards for future articles about how such features as @observables play into the mobx and React environments.

Explore JavaScript Training Courses

Async and Await

More than almost any other language, JavaScript has struggled with a seamingly simple problem – how do you keep interfaces responsive when you have to fetch (or send) contents over a data socket across the web? The first solution to that was to write polling routines to activate a setTimeout or setInterval call, then returning a flag state that indicated a given transfer was complete. With the advent of AJAX calls in the late 1990s, this functionality was relegated to a specific object, the XmlHttpRequest object, and later eventually subsumed in jQuery oriented ajax(), get() and post() functions.

This, in turn, introduced the notion of asynchronous callbacks into Javascript – passing a function as an argument to another asynchronous function with a predetermined set of parameters. Such callback functions would then be invoked once either the data had completed transferring or an error had occurred (in which cases a different function would be passed for cleaning up the action).

One problem became quickly evident with this approach. The resulting callback functions themselves often needed to push the resuting data to another function, which would require another callback, and eventually the resulting code became hideously deep and complex.

The first solution to this problem was to implement a construct called a promise. A promise was a deferred callback object that was implemented initially in ES2015, then refined in ES2016. A promise was a wrapper object that held the callback function(s). When the invoked asynchronous function completed, then it would return a resulting object that could then be passed into a new promise, resulting in a promise chain.

This construct was better, but could still end up being too verbose, especially when what you needed was data from multiple sources independently. The async identifier, along with the await keyword, is the ES2017 solution to that particular problem. As a simple example , first create a promise, in this case a promise of a function that returns a value after a specified number of seconds:

Here, the function resolveAfter interval takes two parameters, a value that will be squared, and the time in seconds. The function creates a new promise around a function (internally called resolve()) that in turn calls a setTimeout function. The resolve function is itself just a placeholder that returns whatever is passed into it, here the square of the number. The t parameter in turn is used to set the number of seconds before the function returns.

The await keyword is applied to a function or variable to indicate that it should await the completion of the promise before passing the results of that promise. If the await is passed on the variables, then the return statement is invoked once the last of the variables are known, here at three seconds.

Note that that await serves very much the same purpose in an asynchronous function that yield does for a generator (and they use many of the same mechanics under the hood). Here, the output will return only once the longest promise’s interval completes, at 3 seconds.

This is a little different from the situation where the awaits is applied to the functions themselves:

In this example, the first await won’t return until after two seconds, while the second await won’t return until three seconds after the first one is satisfied (or five seconds after the code starts). This occurs because the await acts like an asynchronous block – in the second example, the following statement won’t occur until after the initial function’s promise is returned, but in the first example, the return statement executes once the variables have been assigned.

Using await is actually quite valuable in situations where you want an action to occur once all of the data is available from all sources, but not a moment after. Ordinary chaining of promises is almost as bad as synchronous processing (since you’re dependent upon waiting for one promise to complete before a second one can start, as the second example shows), but with the async and awaits keywords you can reduce this wait only to that of the longest single process.

Exponentiation and the Rest Operator

While on the topic of squaring values, another new feature in the ES2017 release is the exponentiation operator “**”. Exponentiation can normally be accomplished via the Math.pow() function, but for complex expressions this notation can be cumbersome. By using the operator, you can replace the expression

with

This can both reduce typing and make the code clearer. For instance, the first function in the previous section can be rewritten as:

It’s worth noting that the ** operator is not quite the same as the Math.pow() function – it has it’s own implementation. This means that if you overload Math.pow() (which might happen if you’re trying to extend Math to complex numbers), the ** operator will not also be overloaded. For instance,

A much richer math package is available at http://mathjs.org. This will let you do things like mathematical operations on vectors and matrices, complex number mathematics, evaluation and solving of algebraic and linear equations, derivative calculus and big number calculations.

While on the topic of operators, one operator that was added in 2016 is worth bringing up as well is the rest operator (“…”). This bit of syntactical magic binds a sequence of parameters to an array of a given name. For example, consider a function that takes a set of strings and returns those as a sorted HTML list structure.

The list() function takes the name of a list wrapping element, an element to wrap each item in the list, then an item itself, which can be a string, an array, or a string, and generates the corresponding output.

An codePen of this can be seen at

Decorators

Decorators are familiar to people who work with Babel, but these (and the associated code constructs that these enable, such as those used by mobx or similar libraries) have yet to make their way into broad implementation natively in most browser engines. As such, you will need to use Babel as a preprocessor for any of the following (or use the ES2015 implementation, discussed below).

Functions are objects. This single fact opens up an entire world in which functions can be “decorated” in various ways in order to expose certain functionality. A decorator, in this context, is a function that wraps around another function in order to provide information to some other process. Decorators differ from ordinary functions in that they do not ordinarily change the result of the function, but rather invoke some additional action when the functon is called, such as adding an entry in a log or indicating that a given parametric class property is observable or not.

The use of such decorators has been around for awhile, and collectively is known as aspect-programming (or, sometimes, metaprogramming). They are, however increasingly showing up in Javascript typically at the point where classes and associated methods are defined. Starting with ES2016, transpilers such as Babel used the @ symbol to indicate such a decorator.

A (relatively) simple example of a decorator might be something like a @log decorator, which is used to identify when a given method is called in a class, along with the arguments applied to that method.

The two functions add and subtract do exactly what you would expect them to do. However, in both cases these methods have the @log decorator placed on it, which serve to add a log event every time each of these methods is invoked:

The log file gives the name of the method and the parameters being passed, along with the time stamp for when the method was called.

In order to create this particular bit of magic, it’s necessary to define the log decorator previously. This would likely be loaded in via an import of some sort from an established library module.The @log decorator itself is defined as follows:

Here, the log function is passed a target (the specific function object, the name of that function, and a descriptor that provides relevant information about the function, such as its passed parameters). The old value of the descriptor (which is a function) is temporarily cached in a variable, a log description is sent to the console, and the function is then invoked with the arguments metavariable passed in through the context of the original function (arguments is an array-like object that holds the arguments of the initial calling function).

Given that you have the function and its associated arguments (and with some work the binding class or prototype) this can not only get information but can also be used to populate other control structures. As an example, certain libraries such as mobx make use of decorators to designate @observable variables. When the value of these change, notifications can be passed back to a reference broker object which will then update items that subscribe to that observable “variable”, without having to write code into the setter/getter directly.

This has incredible utility for React and similar libraries, as these will change UI only when observed variables change. Indeed, this is where the true power of decorators come in: the act of invoking methods can be passed on to specialized objects without the original author of those methods needing to know the internal mechanisms involved.

Object Entries and Values

One JSON design pattern that can be frustrating with current functionality is when you have an object database that you want to use map() and related functions over. For instance, consider the following “database” with associated keys:

Currently in order to use the mapping functions of forEach(), map() and filter() (which are exceptionally handy for writing code) you have to convert the object map into an array using the .from() function, or you have to use a for ([key,value] of obj) type expression. ES2017 introduces the functions Object.values() and Object.entries() to retrieve the object values as a singleton array, or to retrieve all the name/value pairs as an array of arrays.

For instance, the following shows how you can use the map() function with the employees object database given above:

  • ##${key} ${value.name} works for ${employees[value.manager]?employees[value.manager].name:’Company’}

).join("\n"); document.querySelector("##employees").innerHTML =

    • ${list}

`;

Here each entry is destructured into a key and value, then passed via an arrow function into a string template that indicates who works for whom. By the way, the expression:

is a conditional expression that tests to see whether the array value employees[value.manager] is a link to another employee (as a string) or a null value, in which case a default value (“Company”) is provided.

The output can be seen in the following codePen:

See the Pen Using entries() to read a local data store. by Kurt Cagle (@kurt_cagle) on CodePen.

With this and the Object.keys() function, arrays and object maps are now on equal footing as for map/reduce type operations.

Trailing Commas

Veteran programmers might have noticed what looks like a typo in the database given here. There is a comma following the final entry. Normally, this would generate an error, because the conventional interpretation of this is that a null value is now being passed as an entry.

As of ES 2017, however, that’s no longer the case. In both arrays and objects, if a comma is the terminating character in a list or object, it is simply ignored. This might seem like a trivial change, but one of the most common syntax errors that programmers run into when writing code is copying a block of code that’s part of a template into another part of the code and grabbing the trailing comma. Now, the Javascript processor simply ignores it.

Note: Forgetting a comma within a sequence or having two commas enclosing an empty sequence will still generate an error, however.

Padding Strings

Part of ES2016 was the introduction of the repeat() function, which made it possible to repeat a particular string of characters a given number of times. While this has proven handy, two other string functions also provide significant utility: [[String]].padStart() and [[string]].padEnd().

These function are used to add filler characters to the beginning and end of a given string. As an example, suppose that you wanted to have a list of files of the form filename001, filename002, …, filename010, filename011, etc. You would use padStart to create the indexed items.

The first argument gives the total length of the string after padding is applied, while the second argument gives the padding character, by default a ‘ ‘ space.

A similar approach can be used to create inline comments of the form:

The following extends the String prototype with makeLabel, which uses both padStart and padEnd:

To create a comment of 60 character, the following would be invoked:

with the result being:

An example illustrating this can be seen in the following Codepen.

[[[pen slug-hash=’Xpxwvg’ height=’300′ embed-version=’2′]]]

The padStart() and padEnd() functions are just beginning to make their way into browsers now, but should be available in the most recent versions of node and related Javascript engines. Check out the Codepen example for appropriate polyfills.

Summary

This article covered most of the recent additions, with two significant exceptions – the pair Object.defineProperties() and Object.getOwnedProperties() and the emergence of shared memory capabilities. Each of these are significant enough in their own right to be covered by separate articles, and won’t be covered here.

ES2017 is the culmination of an upgrade cycle that has significantly changed the flavor of the Javascript language, bringing it more in line with contemporary functional languages such as Haskell or Scala. The language that has emerged is becoming quite powerful and expressive. At the same time, there is a huge amount of innovation occurring with various libraries such as mobX, React and elsewhere, and these in turn are becoming grist for strengthening the core language to better reflect these innovations.

The following two tabs change content below.

Kurt Cagle

Kurt Cagle (kurt.cagle@gmail.com) is an author of twenty books, blogger, and software architect, and is available for consulting or hire. He lives in Issaquah, WA with his wife, daughters, and cat.

Latest posts by Kurt Cagle (see all)