Quick Links

The annual release of ECMAScript is here, bringing new features and syntax improvements. The specification was finalized by the ECMA International standards organization on June 22, 2021, indicating it's now ready to use.

ECMAScript's most popular implementation is JavaScript, the language supported by web browsers and cloud environments via runtimes like Node.js. Here's a rundown of the major changes in ES2021.

Promise.any()

        Promise.any()
    

completes the aggregate error handling support for promises. It will short circuit and return as soon as one of the passed

        Promise
    

objects is fulfilled.

If all the promises fail, the method rejects with a new

        AggregateError
    

. This object encapsulates the rejection reasons for each of the individual errors.

const promises = [doSomething(), doSomethingElse()];
    

try {

// This will be the return value from `doSomething()`

// or `doSomethingElse()`, depending on which

// resolves first

const firstToResolve = await Promise.any(promises);

}

catch (errors) {

for (const error of errors) {

// Iterate the individual Promise rejection reasons

}

}

        Promise.any()
    

is useful in scenarios where you need to take action if at least one promise resolves but it doesn't matter if some fail. It complements the existing

        Promise.all()
    

and

        Promise.allSettled()
    

methods.

Here's a quick summary of the differences:

  •         any()
        
    - Resolves if any promise resolves. Rejects if all promises reject.
  •         all()
        
    - Resolves if all promises resolve. Rejects if any promise rejects.
  •         allSettled()
        
    - Resolves once all promises have either resolved or rejected. This method does not reject.

Logical Assignment Operators

ES2021 comes with some long-requested operator improvements. New logical assignment operators provide convenient short-circuiting that results in less code to write.

The three operators (

        &&=
    

,

        ||=
    

and

        ??=
    

) combine their corresponding logical comparison with a value assignment. You no longer need to write the variable name twice if you're only assigning when the comparison matches:

// ES2020
    

x && (x = y)

x || (x = y)

x = x ?? y

// ES2021

x &&= y;

x ||= y;

x ??= y;

This helps to round off the operator support and reduces the repetitiveness of assignment-heavy code. It gives logical operators the same assignment integration as the mathematical operators (

        +=
    

,

        -=
    

,

        *=
    

).

String.prototype.replaceAll()

A new method on the

        String
    

prototype makes it much easier to replace all instances of a sub-string, without resorting to a regex. When the existing

        replace()
    

method is given a string as its input, it only replaces the first instance of that value.

        replaceAll()
    

adds the behavior many developers have wanted and expected.

const str = "xxxyz";
    

// xxyz

console.log(str.replace("x", ""));

// yz

console.log(/x/g, "");

// yz

console.log(str.replaceAll("x", ""));

It's a little feature that should have immediate real-world utility. A

        replaceAll()
    

call increases the readability of code compared to a regex with a

        g
    

(global) modifier. This is especially true when you're working with less experienced developers who may be unclear on why the regex was used.

Numeric Separators

A simple change adds numeral separators to the basic JavaScript syntax. This lets you place underscores between digits in literal numbers, making them more readable. They'll be automatically stripped out when your files are parsed.

// ES2020 - This is hard to see at a glance!
    

const billion = 1000000000;

// ES2021 - This can be more readable

const billion = 1_000_000_000;

Separators mimic the way in which commas are used between groups of written numbers. Their use is entirely optional. The syntax also works for binary and hexadecimal literals prefixed with

        0b
    

and

        0x
    

.

Advanced Features

ES2021 includes some new objects aimed at more advanced use cases. First, there's

        WeakRef
    

, a mechanism that holds a reference to an object but keeps it eligible for garbage collection. The reference is "weak" because there's no guarantee it'll be usable in the future.

You'll be able to access the object until it's garbage collected. It might not be garbage collected at all, as it's up to the browser when to remove unreferenced objects.

You create weak references with the

        WeakRef
    

constructor, passing in the object you want to use. You can access the original object later by calling

        deref()
    

.

const x = {};
    

const xWeakRef = new WeakRef(x);

const deRef = xWeakRef.deref(); // this is the object from `x`

Weak references should be used to cache large objects which aren't essential to your application. The browser will be able to clean them up in times of low memory, improving overall performance.

Alongside weak references, the new

        FinalizationRegistry
    

constructor provides notifications when objects are garbage collected. It's not possible to cancel the garbage collection; this feature is intended to let you respond to clean-ups by freeing up any other associated resources. As an example, if an object representing a user was garbage collected, you could use the callback to delete a cached blob of their profile picture.

const registry = new FinalizationRegistry(key => {
    

// This callback will run when a

// registered object is garbage collected

if (key === "user") {

// Delete cached user data here.

}

});

const user = {Username: "CloudSavvy"};

registry.register(user, "user");

In this example, your callback will run and receive

        user
    

as its parameter when the

        x
    

object is garbage collected. You can use the key to identify and remove similar data. You'll need to create this mechanism yourself.

Conclusion

ES2021 is a healthy specification update that brings new convenience features to the ECMAScript community.

        Promise.any()
    

completes the promise handling line-up, while logical assignment operators and

        replaceAll()
    

enhance everyday ease-of-use.

Although only just finalized, ES2021 features have been shipping in browsers all year. Chromium, Firefox, and Safari already offer full ES2021 support, so you can write code using the new capabilities today. Features such as

        Promise.any()
    

and

        String.prototype.replaceAll()
    

can be readily polyfilled for older browsers; others such as logical assignment operators can be handled by pre-processors like Babel.