dg.

A state of Javascript 2021

5 minutes / 946 words

We saw a lot of great improvements to the JavaScript language in 2020 with the release of ES2020 in June last year. I won't cover all of the changes, but I will briefly mention a few of my favorite features that I use on an (almost) daily basis. I'll also talk about some of the changes that will be coming in ES2021 in June later this year.

Features in ES2020

1. Nullish coalescing

This one should be familiar to all of you out there using TypeScript 3.7+. I've certainly been using it for some time now and it's great to see it officially integrated in ES2020. Essentially, the new ?? operator enables a fallback to a specific value when an undefined or null is encountered. Here's an example:

1/** Fallback if "potentiallyNullString" is null or undefined */
2const someValue = potentiallyNullString ?? "A default string";
3
4/** Functionally equivalent to */
5const someValue =
6 potentiallyNullString !== null && potentiallyNullString !== undefined
7 ? potentiallyNullString
8 : "A default string";

Not only does the nullish coalescing operator make the code block more concise, it also provides some solid protection for previous use cases of the || operator. Here's another example:

1const someValue = emptyString || "A default string";

It's possible that in the above code block, the someValue variable should default to an empty string. However in this case, it won't, it will default to A default string due to the logical or operator and the fact that the empty string is falsy. The nullish coalescing operator introduces safeguards and provides great handling for edge cases when encountering falsy values such as 0, NaN or ''.

2. Optional chaining

This one goes hand in hand with the nullish coalsecing operator. Also introduced in TypeScript 3.7, optional chaining allows code to stop executing if a null or undefined is encountered. Optional chaining is even more useful when combined with the nullish coalsecing operator to set defaults. Here's an example:

1/** Optionally call a function or return an empty array */
2const result = state.value?.action() ?? [];

Optional chaining also simplifies code that checks for properties on objects:

1/** Before */
2if (state && state.value && state.value.x) { ... }
3
4/** After */
5if (state?.value?.x) { ... }

3. Promise.allSettled

Similar to Promise.all or Promise.race, this enhancement runs all promises regardless of the results. This is especially useful when performing several network requests in parallel, where some of the requests might either fail or return an error, and others might succeed. Promise.allSettled shines when each request is not dependent on another to complete successfully.

1/** Perform the network requests */
2const requests = await Promise.allSettled([
3 fetch(...),
4 fetch(...),
5 fetch(...)
6]);
7
8/** Split the requests into successes and failures */
9const [errors, results] = requests.reduce((accum, request) => {
10 if (request.status === 'rejected') accum[0].push(request);
11 else if (request.status === 'fulfilled') accum[1].push(request);
12 return accum;
13}, [[], []]);

Features in ES2021

1. Logical assignment operators

Three new operators are proposed for the release in mid of this year. The &&=, ||=, and ??= operators all allow conditional variable assignment. Here are a few examples:

1/** Assign y to x if x is falsy */
2const x ||= y;
3
4/** Assign y to x if x is truthy */
5const x &&= y;
6
7/** Assign y to x if x is null/undefined */
8const x ??= y;

I don't immediately see myself using these operators, but I will keep them in mind. Personally, I think the last one, the ??= would be the most useful.

2. String.replaceAll

Yes! I feel like I have been constantly reimplementing this across different projects, so it is great to see it added as a core functionality. As you might have guessed, replaceAll replaces all occurances of a string/pattern with the second parameter passed into it. Consider it a sequal to the built in replace function for strings.

1const str = "Bob is a builder. Bob builds the best fences.";
2
3const first = str.replace("Bob", "Tom"); // Tom is a builder. Bob builds the best fences.
4const second = str.replaceAll("Bob", "Tom"); // Tom is a builder. Tom builds the best fences.

3. Promise.any

Similar to Promise.allSettled but with a twist. The new .any() method on Promise takes an array of promises and resolves with the first (read: fastest) successful promise. Otherwise, it rejects if all promises reject. On the surface, this doesn't seem all that useful, however one interesting use case could be loading external resources from the fastest server. Consider a browser client application with the following:

1const resource = await Promise.any([
2 fetch("https://us.example.com/resource-a").then((response) =>
3 response.json()
4 ),
5 fetch("https://uk.example.com/resource-a").then((response) =>
6 response.json()
7 ),
8 fetch("https://jp.example.com/resource-a").then((response) =>
9 response.json()
10 ),
11]);

Notice how in the example above, we are requesting a resource from a region specific endpoint. Using Promise.any enables this kind of functionality, where the application interacts with the fastest server relative to the geographical location of the client.

4. Numeric separator and Weakref

Lastly, there are two other new features introduced. The first, the Numeric separator is a readability improvement for displaying large numbers, i.e. 123456789 can become 123_456_789 while still behaving as a number.

The second feature, Weakref is more complex and probably deserving of its own article. The TC39 proposal even mentions that it should be avoided if possible so I won't be covering it here.

That is mostly it! I'll be looking forward to next year and seeing what will make it into ES2022.


Have I made a mistake? Please consider submitting a pull request
Back to top