ES 2021 - New Additions to the Browser Near You
7 min read • –––
ES 2021 got standardized few days back(22nd June), and ES6 now supports few really exciting features that contribute towards improving the readability of the language syntax as well as add to the construct repertoire available to deal with string replacement, asynchronicity, and performance.
Let's see what these new additions are!
String replaceAll()
or, one method to replace `em all! 💪
Javascript has long needed a way to replace all the occurrences of a string with provided string, and this method does exactly what we already expect by its name.
Although, javascript has a String.prototype.replace
method it can only replace the first occurrence.
const str = "Mandalorian knows Mandorian, but Mandorians dont know Mandalorian";
const updatedStr = str.replace("Mandalorian", "Bobba")
console.log(updatedStr); // Bobba knows Mandorian, but Mandorian dont know Mandalorian
If we need to carry out full replacement we'll have to express the match value as a regular expression
const str = "Mandalorian knows Mandorian, but Mandorians dont know Mandalorian";
const updatedStr = str.replace(/Mandalorian/g, "Bobba")
console.log(updatedStr); // Bobba knows Mandorian, but Mandorians dont know Bobba
With String.prototype.replaceAll
we now can carry out a full replacement without relying on regex patterns
const str = "Mandalorian knows Mandorian, but Mandorians dont know Mandalorian";
const updatedStr = str.replaceAll("Mandalorian", "Bobba")
console.log(updatedStr); // Bobba knows Mandorian, but Mandorians dont know Bobba
Promise's Promise.any
or, just gimme what resolves first! 💨
Promise.any
is polar opposite of Promise.all
, and it prioritizes the promise that resolves first, and could compensate for latency in scenarios with multiple independent parts where the success of one participant promise is all that's needed. Unlike Promise.all
it does not waits for the fulfilment of all participating promises or first rejection to resolve to let the dependent code act. For the following promises that resolve at different times.
const fetchTheMando = new Promise((resolve, reject) => {
setTimeout(
() => resolve("The Mandalorian is here now!"),
Math.floor(Math.random() * 100)
);
});
const fetchBobba = new Promise((resolve, reject) => {
setTimeout(
() => resolve("Bobba Fett is here now!"),
Math.floor(Math.random() * 100)
);
});
const fetchLuke = new Promise((resolve, reject) => {
setTimeout(
() => resolve("Luke Skywalker is here now!"),
Math.floor(Math.random() * 100)
);
});
it could be simply used like
(async function() {
// Fetch one and save the child!
const result = await Promise.any([ fetchTheMando, fetchBobba, fetchLuke ]);
console.log(result); // Prints "The Mandalorian/Boba Fett/Luke Skywalker is here now!" for whatever promise resolves first
})();
When any of the participant promises fail, you get a AggregateError
that could be handled in a try/catch block like other exceptions.
const fetchTheMando = new Promise((resolve, reject) => {
setTimeout(
() => resolve("The Mandalorian is here now!"),
Math.floor(Math.random() * 100)
);
});
const fetchMoff = new Promise((resolve, reject) => reject());
try {
(async function() {
const result = await Promise.any([ fetchTheMando, fetchMoff ]);
})();
} catch(error) {
console.log(error.errors);
}
Promise.any
is similar toPromise.race
, except it, doesn’t reject early when one of the promises rejects.
Logical Assignment Operator
or, I ❤️ shorter short-cuts!
If expressing a condition like x && x = y
or if (x) { x = y }
feels verbose, repetitive, or tiring to you the logical assignment operators will prove to be a good friend. You can express the aforementioned expressions simply as x &&= y
now. The new syntax combines the logical operations(&&, || or ??) with assignment.
What's more amazing is that this feature extends beyond logical and &&
to logical OR ||
and the Nullish Coalescing operator ??
too. Let's see how these could be applied.
Logical AND &&
let x = 1;
let y = 2;
// pre-ES 2021
x = x && y
// Now
x &&= y; // x will be 2
Logical OR ||
let x = 1;
let y = 2;
// pre-ES 2021
x = x || y;
// now...
x ||= y; // x will be 2 as the assignment will only happen when x is falsy
Null Coalescing ??
let x;
const y = 2;
// pre-ES 2021
x = x ?? y;
// now...
x ??= y; // as x is undefined, x will be 5 post this
Numeric Separators
or, I luv 🔢 neat!
Underscore(_) as separators has been available in many programming languages, and can greatly improves the readability in representing numeric and bigint
values. Here's how you can use it
// pre-ES 2021
const bounty = 1000000000; // Is this a billion? a hundred million? Ten million?
// now...
const bounty = 1_000_000_000_000; // neat!
this can be applied to binary
let beskar = 0b1010_0001_1000_0101;
and hex literals too
let messageFromMarshal = 0xA0_B0_C0;
WeakRef and FinalizationRegistry
or, I like being 🧠 mindful towards memory!
WeakRef
and FinalizationRegistry
serve two functions
- creation of weak references to objects with the
WeakRef
class - running user-defined finalizers after objects are garbage-collected
and could be used independently or together, depending on the use-case.
A WeakRef
(weak reference) to an object is a reference that won't prevent the object it refers to, from being garbage collected. Even, when garbage collector sees the referred object referenced in a non-collectible path.
Consider the following code
const mandoCalls = () => {
const starWarsGalaxy = {
entities: {
mando: {
title: "lone bounty hunter",
bio: "travels in the outer reaches of the galaxy, far from the authority of the New Republic"
},
...
},
travels: {
...
}
};
console.log(starWarsGalaxy);
}
(async function(){
await new Promise((resolve) => {
setTimeout(() => {
mandoCalls();
resolve();
}, 2000);
});
})();
When the above code is executed, starWarsGalaxy
will be printed after 2 seconds. Based on how we use the mandoCalls
, starWarsGalaxy
may remain stored in memory forever.
A WeakRef
will let you declare this object as a weak reference, and makes it ephemeral in a non-predictable world.
const mandoCalls = () => {
const starWarsGalaxy = new WeakRef({
entities: {
...
},
locations: {
...
}
});
console.log(starWarsGalaxy.deref());
}
(async function(){
await new Promise((resolve) => {
setTimeout(() => {
mandoCalls(); // Guaranteed to print starWarsGalaxy entries
resolve();
}, 2000);
});
await new Promise((resolve) => {
setTimeout(() => {
mandoCalls(); // No Gaurantee that starWarsGalaxy is still attached
resolve();
}, 4000);
});
})();
Use of the target or referent object starWarsGalaxy
is guaranteed to work as expected in the first mandoCalls
but has zero chances of yielding in the subsequent calls. As referents are wrapped in a WeakRef
, their access approach also changes to referent.deref()
instead of direct access.
WeakRef
prove useful in implementing cache or mappings to large objects where you'd not prefer to hold the referent objects too long after use.
FinalizationRegistry
as a companion feature lets you listen to garbage collection on objects you're interested in.
Once you have a FinalizationRegistry
instance, with a cleanup callback
const registry = new FinalizationRegistry((heldValue) => {
// do something with the held value
});
You can register the objects you'd like the callback to be executed for (post garbage collection) with the register
method
registry.register(theInterestingObject, "pass this on as the held value!");
Execute something like
(function () {
const starWarsGalaxy = {};
registry.register(starWarsGalaxy, "Bobba says bye");
})();
and the passed message Bobba says bye
will be received by the cleanup callback, registered while instantiating FinalizationRegistry
as soon as starWarsGalaxy
is garbage collected.
Conclusion
You might not find immediate use for these new additions, but they're noteworthy enhancements from a language maturity point of view. All of these features are available to the evergreen browsers near you, and could be easily configured with babel for use.
Thank you for being till the end 🙌 . If you enjoyed this article, or learned something new, please take a second to tweet this article or share on LinkedIn so others can discover it. Or add to the discussion on Twitter.