The 5 most transformative JavaScript features from ES11
JavaScript has come a long way in the past 10 years with brand new feature upgrades in each one.
Let’s take a look at the 5 most significant features that arrived in ES11; and see the ones you missed.
1. Modularization on the fly: Dynamic import
s
ES11 was the awesome year when import
could now act as function, like require()
.
An async
function.
Keeping import
s at the top level was no longer a must; We could now easily resolve the module's name at compile time.
Loading modules optionally and only when needed for high-flying performance...
async function asyncFunc() {
if (condition) {
const giganticModule = await import('./gigantic-module');
}
}
Loading modules based on user or variable input...
import minimist from 'minimist';
const argv = minimist(process.argv.slice(2));
viewModule(argv.name);
async function viewModule(name) {
const module = await import(name);
console.log(Object.keys(module));
}
It's also great for using ES modules that no longer support require()
:
// ❌ require() of ES modules is not supported
const chalk = require('chalk');
console.log(chalk.blue('Coding Beauty'));
(async () => {
// ✅ Runs successfully
const chalk = (await import('chalk')).default;
console.log(chalk.blue('Coding Beauty'));
})();
2. Promise.allSettled()
But we already had Promise.all()
to wait for all the Promise
s in a list to resolve?
const responses = await Promise.all([
fetch(endpoint1),
fetch(endpoint2),
]);
So what was the point of this, right?
But no, allSettled()
turns out to be quite different from all()
.
Promise.all()
: If even a single Promise
in the list fails, everything fails.
But you see the problem: We'd have no idea if one failed and which succeeded.
What if you want to retry the failed errors until they do succeed? You're stuck.
Until you turn to Promise.allSettled()
:
❌ Before ES11:
// ❌ Promise.all()
async function fetchData() {
const apiUrl = 'api.tariibaba.com';
const endpoint1 = `${apiUrl}/route1`;
const endpoint2 = `${apiUrl}/route2`;
try {
const responses = await Promise.all([
fetch(endpoint1),
fetch(endpoint2),
]);
} catch (err) {
// ❌ Which failed & which succeeded? We have no idea
console.log(`error: ${err}`);
}
// ...
}
✅ After ES11:
Promise.allSettled()
: Wait for every Promise to fail or resolve.
// ✅ Promise.allSettled()
async function fetchData() {
const apiUrl = 'api.tariibaba.com';
const endpoint1 = `${apiUrl}/route1`;
const endpoint2 = `${apiUrl}/route2`;
try {
const promises = await Promise.allSettled([
fetch(endpoint1),
fetch(endpoint2),
]);
const succeeded = promises.filter(
(promise) => promise.status === 'fulfilled'
);
const failed = promises.filter(
(promise) => promise.status === 'rejected'
);
// ✅ now retry failed API requests until succeeded?
} catch (err) {
// We don't need this anymore!
console.log(`error: ${err}`);
}
// ...
}
3. Optional chaining
?.
is all over the place now but it all started from ES11.
We've been checking vars for null
since the dawn of time but it gets pretty cumbersome when we're null-checking nested properties.
❌ Before ES11:
const a = {
b: {
c: {
d: {
site: 'codingbeautydev.com',
name: null,
},
},
},
};
// ❌ Must check every property for null
if (a && a.b && a.b.c && a.b.c && a.b.c.d.site) {
console.log(a.b.c.d.site);
}
✅ After ES11:
With the optional chaining operator:
// `?.` auto-checks every property for null
// if any prop in the chain is null, short-circuit
// and return null
if (a?.b?.c?.d?.site) {
console.log(a.b.c.d.site);
}
4. Nullish coalescing
??
was one of the most impactful JavaScript additions.
A powerful to set a variable to a default value and use it at the same time.
❌ Before ES11:
We repeat the variable unnecessarily:
console.log(str1 ? str1 : 'codingbeautydev.com'); // coding is cool
console.log(str2 ? str2 : 'codingbeautydev.com'); // codingbeautydev.com
✅ After ES12:
??
keeps things clean and readable:
const str1 = 'coding is cool';
const str2 = null;
console.log(str1 ?? 'codingbeautydev.com'); // coding is cool
console.log(str2 ?? 'codingbeautydev.com'); // codingbeautydev.com
It coalesces.
Left and right combine to form a non-null whole:
5. Go big or go home: Big Ints
The name BigInt
gives it away: loading up on humongous integer values:
const bigInt =
240389470239846028947208942742089724204872042n;
const bigInt2 = BigInt(
'34028974029641089471947861048917649816048962'
);
console.log(typeof bigInt);
console.log(bigInt);
console.log(typeof bigInt2);
console.log(bigInt2);
console.log(bigInt * bigInt2);
Because normal integers can't:
// ✖️ Stored as double
const normalInt = 240389470239846028947208942742089724204872042;
const normalInt2 = 34028974029641089471947861048917649816048962;
console.log(typeof normalInt);
console.log(normalInt);
console.log(typeof normalInt2);
console.log(normalInt2);
// ✖️ Precision lost
console.log(normalInt * normalInt2);
Final thoughts
These are the juicy new JavaScript features that arrived in the ES12.
Use them to boost your productivity as a developer and write cleaner code with greater conciseness, expressiveness and clarity.
See also
- The 5 most transformative JavaScript features from ES12
- The 5 most transformative JavaScript features from ES13
- The 5 most transformative JavaScript features from ES8
- The 5 most transformative JavaScript features from ES9
- The 5 most transformative JavaScript features from ES14
- The 7 most transformative JavaScript features from ES10