Development

JavaScript Forecast: What’s Ahead for ECMAScript 2022?

25 Oct 2021 5:00am, by
JavaScript logo
Editor’s Note: This article has been updated to clarify which private class proposals have already reached stage four.

JavaScript continues to evolve, and the next annual update to ECMAScript, which formally standardizes the JavaScript language, will be approved in July 2022. That will include all the new feature proposals that have reached stage four by March 2022 — proposals that have been signed off by the ECMAScript editors, passed the test suite and shipped in at least two implementations.

A handful of proposals are already at that stage. And you can get an idea of what other features may make it into ECMAScript 2022 by looking at which have reached stage three, where the spec has been signed off and the tests have been passed. But these proposals need to be tried out in an implementation to see how well they actually work in practice.

For instance, the proposal to be able to work backward through an array starting with the last element the way you can in Python is currently implemented as a polyfill, in an attempt to discover if it clashes with any commonly used JavaScript frameworks.

We asked two of the co-chairs of the TC39 committee that standardizes ECMAScript, Rob Palmer (head of Bloomberg’s JavaScript infrastructure and tooling team) and Brian Terlson (principal architect on the Azure SDK for TypeScript and JavaScript, and former editor of the spec) to pick out some of the most significant proposals — including major improvements to working with dates.

Top-Level Await

JavaScript has had asynchronous functions since ECMAScript 2017, but developers have to explicitly declare a function to be asynchronous.

“If you’re running a module initializer — the top level of your code that runs when your module begins — that hasn’t always been regarded as asynchronous,” Palmer said.

Using the await function to wait on an asynchronous process as that top-level might result in a syntax error if other code tries to read results that haven’t yet been returned, and attempts to avoid that by using async main make code more complicated and harder to test or perform static analysis on.

With Top-Level Await (which has reached stage four and is already implemented in the three main browser JavaScript engines), the module system coordinates all the async promises for you.

Top-Level Await is important for integrating WebAssembly modules with JavaScript.  This will also help developers who share samples and code snippets, said Terlson: “I don’t need to put the async function name around them to make them copy/paste-able.”

It’s taken a while for Top-Level Await to become part of ECMAScript because of concerns that it would make it more likely that developers would create deadlocks in their code.

“When we allow asynchronous operations to be put in there, people can write arbitrarily long-running operations — like querying a database, making a network request,” Palmer warned.

“If you are not careful with this feature, you could delay your entire app loading. You can’t even run the first main function because you’re now waiting on some arbitrary operation to complete.”

But calling async code is very common in browsers, he added, because if you have to wait for a piece of code to finish running before anything else can run, blocking code can cause performance issues.

With Top-Level Await, ECMAScript is diverging from Node.js, which uses CommonJS modules, which will remain synchronous for backward compatibility. Developers won’t be able to load ECMAScript modules synchronously in a CommonJS file; they should use the dynamic import operator, which returns not the module but a promise for the module, loading it asynchronously.

Finally, Private Fields

By default, the properties in JavaScript classes are public and can be called from anywhere in your code. But sometimes that exposes some fields that you don’t want to have accessed (and possibly changed) from elsewhere in the code: you might want to have a stable interface in a library but be able to change the code that implements the class over time, which means making sure developers don’t take a dependency on any specific implementation detail.

So far, the convention has been to mark private fields by using an underscore at the beginning of the property name, but the JavaScript language hasn’t done anything to block access from outside the class. With private class fields, using # to start the field name means it can only be accessed directly inside the body of the class in which it’s declared.

Discussions about implementing private fields have gone on for a number of years (and they’re already available in Node.js). The suite of proposals that make up the new class fields is ready to be part of ECMAScript 2022, with a mix of proposals for public, private and static fields, methods and accessors, including a way to check if a private field exists.

All the browsers are now implementing key pieces. Being able to declare instances of static fields and make them private for data encapsulation has already been implemented in Chrome and Safari.

That unlocked the key proposals going to stage four earlier this year, according to Palmer.

Temporal, for Date-Related Javascript

Temporal, which Terlson refers to as “the replacement for our broken Date object,” reached stage three earlier this year. It’s set to replace libraries like Moment.js, which have done a good job filling a gap in JavaScript that’s so common that it makes more sense to have the functionality as part of the language.

Temporal is a global object that will be a top-level namespace for the new date and time API, covering the full range of date, time, time zones, calendars and even public holidays.

Whether you need the current time, a Unix timestamp, the day of the week a date falls on in a particular year, how many days till Christmas, whether a business that lists opening hours is open or closing in the next few minutes, an easy way to sort dates and times or something more complex involving multiple time zones, the Temporal API covers it.

“Temporal is going to completely change the way we write date-related JavaScript code, [by] being able to actually do date math without falling into myriad different pitfalls,” Terlson said. “It has good ways to represent a calendar date or a time that might exist on a clock: these are things that we just have no way to do in JavaScript right now, it always has to be attached to a time zone and a particular UTC instance, which causes no end of problems.”

Going from a single Date object to multiple options in Temporal may feel like more work, but Terlson suggested it will be easier to use in practice because it’s more rigorous.

“The Date object that we have now only feels easy because you don’t realize you’re shooting yourself in the foot half a dozen times,” he said. “The new date system forces you to think about that complexity upfront, and so you’re just frontloading your bug-finding efforts.

Temporal is set to replace libraries like Moment.js, which have done a good job filling a gap in JavaScript that’s so common that it makes more sense to have the functionality as part of the language.

“Dates are one of those things where it’s hard to test; you can’t just change the system date easily in the middle of unit tests,” he added, “The right way to do it is to mock it — but a lot of people just don’t touch their date code so they don’t find [the problems.”

In addition, Terlson said, “you almost need to be a data expert to know what the interesting test cases are.” Not every developer would think to test for leap year behavior or what if their code will run correctly when a leap second is declared, he suggested. “Or a mobile app when the user changes time zone — is it going to fall over?”

Palmer calls Temporal “in some ways the least controversial proposal” he’s seen. Everyone wants to see it progress, he noted —  but because it’s such a large API, fully implementing and testing it and taking feedback may take a little time.

However, production-quality polyfills are already in development, and there’s a sandbox in the Temporal documentation powered by an earlier polyfill, so developers can try it out in their browser developer tools.

He also sees strong developer demand and expects Temporal to start getting used quickly, so developers can remove Moment.js from their applications, “making everyone’s code lighter and faster to download.” But the sheer size of the API means testing will take time.

“The reason these libraries are so big and complex is not accidental complexity,” Palmer said. “It is because the rules in our world for dates and times are so complex that you want to offload as much as much as you can to the experts that have thought about this and embedded all that knowledge in the API.”

Moving Temporal to stage four will also have to wait until the Internet Engineering Task Force (IETF) work to standardize the string formats used for calendar and time zone annotations is complete; that’s expected for 2022.

Chaining Error Cause

Also at stage three is a proposal Terlson views as a companion to the aggregate error that was standardized with Promise.any in ECMAScript 2021: Error Cause, which is already implemented in Chrome, Firefox, Safari and Node.js.

“Aggregate error is used for cases when you have different sources of errors: where you’re doing different processes and you get a bunch of errors that are only related by the fact that they are kicked off by the same activity,” Terlson said.

The Error Cause proposal is a way to link errors that cause other errors, like making a network request as part of a bigger operation that will use that network request. Often developers will just throw an error if the network request fails, which doesn’t provide any context for which operation failed because of the network request failure.

With Error Cause, developers can throw an error including an error code with a cause property that records how many requests filed, each of which has its own error with more information, and those can chain more errors in turn.

“It lets you catch an error and throw an error with more context, while preserving the original error,” Terlson said.

There are also some interesting proposals that have only reached stage two —where there’s a full specification and it’s likely the proposal will continue to be developed and become part of ECMAScript, but there’s not enough agreement to sign off a complete specification that’s ready to be tested in implementations.

Record and Tuple for Better Matching

User interface developers will be interested in the stage two Record and Tuple proposal that provides first-class support for the immutable data structures often used in interface development. Today that’s done with libraries like Immutable.js.and Immer, but having this in the JavaScript language will make the new Record and Tuple data structures easier to debug.

Record is similar to a frozen object and Tuple is similar to a frozen array,” Palmer said. Because they’re primitives rather than objects (which have identity) and they’re compared by value, you can compare two separate objects that contain the same things – like a person’s name – and they’ll be recognized as being the same, even though they’re in different objects, each of which has its own identity.

If you’re using that name as the key to a map, you want to be able to do a lookup in some other part of your code, where you generate a fresh record but want to find out if that person’s name is in the map. With Record and Tuple, you’ll get the match instead of it failing because the fresh record is a brand new identity object.

“Previously, people had to do tricks to try and make maps work well when you want to use a composite key with multiple things, like serializing the contents into a string with special markers between the different fields, so that the strings would be compared by value,” Palmer said.

Reading the contents of an object out into a string is an opportunity to introduce bugs; now you can use proper language structures and use the regular triple === equality comparison.

This will be particularly useful for comparisons in JSON-style deep object trees, Palmer suggested. “You’re getting a more semantic style of equality.”

Using === can also improve performance and reduce the compute load of rendering the user interface.

“A common functional pattern, like you see in React, is where you’re examining your data properties, and seeing how they changed since the last render,” Palmer said. “You can do optimizations: if my input data has not changed, I would expect the rendering map remains identical, so you don’t need to recompute it.”

He views the syntax as less cumbersome than the existing libraries: “This provides a really, terse, really clear way of dealing with immutable data structures. Also, if you’re a library writer, you now have a common currency to exchange with the rest of the world. It standardizes the way of passing around immutable data.”

Pipeline Operator Still in the Pipeline

Some other interesting proposals are less likely to make the ECMAScript 2022 timeline.

Most scripting environments have a pipeline operator that lets you take the output of one function and “pipe” it into another function (pipes are also widely used in functional programming). Without a pipe operator, you have to build a hierarchy of nested functions which can be hard to read, or create — and manage — temporary intermediate variables to store and pass results from function to function.

So far, JavaScript hasn’t had a native pipe option (although there are many libraries that add it), and adding this was one of the top four requests in the State of JS 2020 survey.

There have been many suggestions for how to do pipes in JavaScript over the years and there are currently several competing proposals following the approach taken in F# or the Hack language.

The Hack-style proposal has reached stage 2 and there are already plugins for Babel and a feature flag in Firefox that support the new operator, but there are also ongoing discussions about concerns on the impact on browser engine memory and performance, so the pipe operator is still definitely experimental and seems unlikely to be in ECMAScript 2022.