What to expect from JavaScript ES2017 — The Async Edition
The JavaScript world moves fast. Thanks to this ‘heat wave’ going on in the JS community, it is now completely normal to wake up and open your Github Explore email to see a new trending framework or library that is able to ‘do everything the last one did, but faster/better’ (read: add some features).
While not the case for all open source packages, it is important to note that some libraries and frameworks are game-changers. Their introduction to the ecosystem changes entire development paradigms and previously undebatable notions and standards. Just think about the difference between web app development with jQuery and React.
Since ES6/ES2015 took roughly 6 years to get released, TC39, the technical committee that oversees and writes the specification for the JavaScript language, decided to adopt a yearly release cycle. This change is largely to allow for smaller and more iterative releases of the language specs. If it so happened that a feature specification was not completely finished by this year's deadline, it could always make it into next year's release.
This is a win for the JS community. A smaller, yet feature-packed, yearly release cycle allows for more consistent growth of the language's specification.
Enter ES2017
I think it is a fair assessment to say that a large portion of JavaScript developers seem to appreciate the new, the shiny, the bleeding-edge tools. Whether it is to completely embrace a new technology, or simply to test out a tool's functionality, we like to know what is out there to at least 'dip our toes in the water'.
If you're like me, keen on knowing about all of the new technology this incredible community has to offer, here is a breakdown of what ES2017 has in store.
Major Features
Async Functions
TJ Holowaychuk is a huge contributor to the JavaScript community. Having worked on projects like Express, Koa, Rework, and Co, TJ's code has, directly or indirectly, become an integral part of a ton of development projects.
His latest efforts on the Co package have had heavy influence on the much-anticipated Async Await feature specification (by Brian Terlson) that was recently moved to Stage 4. Co is a library that leverages Promises and Generator Functions to allow for asynchronous JavaScript code syntax that reads more synchronously.
A popular flow to handle Async Functions with Promises looks like this:
function fetchData(url) {
return fetch(url)
.then(request => request.text())
.then(text => {
return JSON.parse(text);
})
.catch(err => {
console.log(`Error: ${err.stack}`);
});
}
With the new async
and await
keywords in ES2017, we can leverage a new, highly Co-inspired, syntax that reads entirely synchronously. We can use of try/catch blocks and the new keywords to assign asynchronous behavior to specific functions. Internally, Async Functions work the same as generators, but they are not translated to Generator Functions. It looks like this:
async function fetchData(url) {
try {
let request = await fetch(url);
let text = await request.text();
return JSON.parse(text);
}
catch (err) {
console.log(`Error: ${err.stack}`);
}
}
You can write Async Functions in ES2017 in the following ways:
Async Function Declarations
async function fooBar() {}
Async Function Expressions
const fooBar = async function () {};
Async Method Definitions
let obj = { async fooBar() {} }
Async Arrow Functions
const fooBar = async () => {};
Shared Memory and Atomics
Lars Hansen put together the proposal for Shared Memory and Atomics for ES2017 and as of February of 2017 it was approved onto Stage 4 and bundled into the specification.
This feature introduces a new low-level Atomics namespace object and a SharedArrayBuffer constructor that serve as primitive buildings blocks for higher-level concurrency abstractions. This enables developers to share bytes of data of a SharedArrayBuffer object between multiple service workers and the core thread. This behavior introduces large gains as you can more easily share data between workers, thus allowing for improved coordination between them.
For more information on the implications of the new Atomics object and SharedArrayBuffer constructor, read a more in depth analysis of it here or read a brief tutorial by Lars Hansen.
Minor Features
Trailing commas in function parameter lists and calls
The trailing commas in functions proposal is a purely syntax update to the spec. Before this spec change, it was not allowed to have a comma after the last function parameter, like so:
const trailCommaFn = function(
param1,
param2,
param3,
param4) { // No comma allowed here!// do something in function body}
ES2017 brings in the trailing comma:
const trailCommaFn = function(
param1,
param2,
param3,
param4, // Comma allowed here!
) {// do something in function body}
This syntax update makes trailing commas in functions more consistent with the rest of the spec. It is already common to use of trailing commas in Arrays and Object Literals, and now we can use of the same behavior to function lists and calls.
// Array
const arr = [
1,
2,
3, // <--- Ok
];// Object Literal
const obj = {
x: 'foo',
y: 'bar',
z: 'baz', // <--- Ok
}
Additionally, this spec change is very git-friendly, as developers no longer need to edit 2 lines of code in order to add 1 parameter to a function list or call. You may now use commas everywhere, even after the last parameter.
Object.values/Object.entries
Object.entries, proposed by Jordan Harband, introduces the notion of an entry
. Objects are data structures of key-value pairs and each key-value pair is an entry
. Object.entries(x)
coerces x
to an Object and returns the entries of its enumerable own string-keyed properties, in an Array (much like Object.keys):
>> Object.entries({ foo: 1, bar: 2 })
[ [ 'foo', 1 ], [ 'bar', 2 ] ]
This in turn, allows for developers to leverage Object.entries to iterate over an object's properties.
Much like Object.entries, Object.values returns an Array with the values of the own enumerable string-keyed properties:
>> Object.values({ foo: 1, bar: 2 })
[ 1, 2 ]
The method signature for both Object.entries()
and Object.values()
is as follow:
Object.entries(value : any) : Array<[string,any]>Object.values(value : any) : Array<any>
String Padding
The String Padding spec feature was introduced to finally give JavaScript some native methods to handle Strings. It exposes padStart and padEnd, as well as trimStart and trimEnd methods, thus giving developers more control over string primitives.
Without a reasonable way to pad a string using native methods, working with JavaScript strings today is more painful than it should be. Without these functions, the language feels incomplete, and is a paper cut to what could be a very polished experience. — TC39 String Padding Proposal Rationale.
>> console.log('testing'.padStart(12).length)
" testing" is 12>> console.log('testing'.padStart(12, '_'))
"_____testing" is 12
Object.getOwnPropertyDescriptors
There is not a single method in ECMAScript capable of simplifying a proper copy between two objects. In these days more than ever, where functional programming and immutable objects are essential parts of complex applications, every framework or library is implementing its own boilerplate in order to properly copy properties between composed objects or prototypes. — Jordan Harband TC39 Proposal Motivation
Object.getOwnPropertyDescriptors()
is a plural function of Object.getOwnPropertyDescriptor()
that aims to simplify the process of copying an object in JavaScript.
When coupled with Object.defineProperties()
, this new spec feature allows for decorators to easily grab all descriptors from another class or mixin and assign them to a new object.
The motivation behind this proposal is also partly due to the often undesired behavior when it comes to duplicating an object using Object.assign()
. It copies objects on a way that swallows behavior — it directly accesses properties and symbols instead of their descriptors, effectively discarding possible accessors. This could become a more hazardous issue when it comes to composing and handling complex objects and classes' prototypes.
With this spec update, developers no longer need to rely solely on Object.assign()
to copy objects, instead leveraging thecreation of a real shallow copy like this:
// Given an object `obj`>> Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
Learn More about the TC39 Process
TC39’s change to a yearly release cycle also modified the way specification proposals are handled. The introduction of proposal stages create better organizational rules and transparency to the open source JS community. Each proposal must go through stages 0
through 4
to eventually become part of the language's spec.
Stage 0
is known as Strawman stage and simply aims to be the initial stage for input into the specification.
Stage 1
is known as the Proposal stage, where the proposer makes the case for the feature addition, describes the solution and identifies any major challenges.
Stage 2
is known as the Draft stage, where proposer must precisely describe the syntax and semantics using formal spec language.
Stage 3
is known as the Candidate stage, where proposer must heavily test such feature/functionality. A proposal in this stage indicates that further refinement will require feedback from implementations and users.
Stage 4
is known as the Finished stage, which indicates that the addition is ready for inclusion in the formal ECMAScript standard.
For further information on each Stage, please refer to the official TC39 Process Document.
There are currently 7 proposals that are on Stage 3 of the process and will most likely reach Stage 4 acceptance for an ES2018 spec release. They are as follow:
- Function.prototype.toString
- global
- import()
- Rest/Spread Properties
- SIMD.JS — SIMD APIs
- Lifting Template Literal Restriction
- Asynchronous Iteration
To find out more about changes and updates to upcoming JavaScript versions, stay up-to-date with the proposals stage list on TC39’s Github Repository.
Use ES2017 Features Now
If you are eager to test some of these ES2017 goodies, especially async
and await
, then head over to Babel's Docs on ES2017 Preset which compiles all ES2017 features to ES2016 code. Additionally, you may also use of Babel's Latest Preset that allows for the compilation of all ES2015, ES2016 and finalized ES2017 features in your codebase.
For a deeper dive into the new ES2017 spec features, I highly recommend checking out Dr. Axel Rauschmayer’s Exploring ES2016 & Es2017 book.
So that's ES2017.
Cheers,
— AN