Skip to main content

Promise combinators

· 5 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Since the introduction of promises in ES2015, JavaScript has supported exactly two promise combinators: the static methods Promise.all and Promise.race.

Two new proposals are currently making their way through the standardization process: Promise.allSettled, and Promise.any. With those additions, there’ll be a total of four promise combinators in JavaScript, each enabling different use cases.

`Array.prototype.flat` and `Array.prototype.flatMap`

· 2 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Array.prototype.flat

The array in this example is several levels deep: it contains an array which in turn contains another array.

const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ outer array
// ^^^^^^^^ inner array
// ^^^ innermost array

Array#flat returns a flattened version of a given array.

array.flat();
// → [1, 2, [3]]

// …is equivalent to:
array.flat(1);
// → [1, 2, [3]]

Numeric separators

· 2 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Large numeric literals are difficult for the human eye to parse quickly, especially when there are lots of repeating digits:

1000000000000
1019436871.42

To improve readability, a new JavaScript language feature enables underscores as separators in numeric literals. So, the above can now be rewritten to group the digits per thousand, for example:

`String.prototype.matchAll`

· 3 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias))

It’s common to repeatedly apply the same regular expression on a string to get all the matches. To some extent, this is already possible today by using the String#match method.

In this example, we find all words that consist of hexadecimal digits only, and then log each match:

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// Output:
//
// 'DEADBEEF'
// 'CAFE'

However, this only gives you the substrings that match. Usually, you don’t just want the substrings, you also want additional information such as the index of each substring, or the capturing groups within each match.

It’s already possible to achieve this by writing your own loop, and keeping track of the match objects yourself, but it’s a little annoying and not very convenient:

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// Output:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

The new String#matchAll API makes this easier than ever before: you can now write a simple for-of loop to get all the match objects.

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// Output:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

String#matchAll is especially useful for regular expressions with capture groups. It gives you the full information for each individual match, including capturing groups.

const string = 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} at ${match.index} with '${match.input}'`);
console.log(`→ owner: ${match.groups.owner}`);
console.log(`→ repo: ${match.groups.repo}`);
}

`Intl.ListFormat`

· 3 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias)) and Frank Yung-Fong Tang

Modern web applications often use lists consisting of dynamic data. For example, a photo viewer app might display something like:

This photo includes Ada, Edith, and Grace.

A text-based game might have a different kind of list:

Choose your superpower: invisibility, psychokinesis, or empathy.

Since each language has different list formatting conventions and words, implementing a localized list formatter is non-trivial. Not only does this require a list of all the words (such as “and” or “or” in the above examples) for each language you want to support — in addition you need to encode the exact formatting conventions for all those languages! The Unicode CLDR provides this data, but to use it in JavaScript, it has to be embedded and shipped alongside the other library code. This unfortunately increases the bundle size for such libraries, which negatively impacts load times, parse/compile cost, and memory consumption.

Public and private class fields

· 4 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Several proposals expand the existing JavaScript class syntax with new functionality. This article explains the new public class fields syntax in V8 v7.2 and Chrome 72, as well as the upcoming private class fields syntax.

Here’s a code example that creates an instance of a class named IncreasingCounter:

const counter = new IncreasingCounter();
counter.value;
// logs 'Getting the current value!'
// → 0
counter.increment();
counter.value;
// logs 'Getting the current value!'
// → 1

Note that accessing the value executes some code (i.e., it logs a message) before returning the result. Now ask yourself, how would you implement this class in JavaScript? 🤔

ES2015 class syntax

Here’s how IncreasingCounter could be implemented using ES2015 class syntax:

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}

The class installs the value getter and an increment method on the prototype. More interestingly, the class has a constructor that creates an instance property _count and sets its default value to 0. We currently tend to use the underscore prefix to denote that _count should not be used directly by consumers of the class, but that’s just a convention; it’s not really a “private” property with special semantics enforced by the language.

`Intl.RelativeTimeFormat`

· 5 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Modern web applications often use phrases like “yesterday”, “42 seconds ago”, or “in 3 months” instead of full dates and timestamps. Such relative time-formatted values have become so common that several popular libraries implement utility functions that format them in a localized manner. (Examples include Moment.js, Globalize, and date-fns.)

Well-formed `JSON.stringify`

· One min read
Mathias Bynens ([@mathias](https://twitter.com/mathias))

JSON.stringify was previously specified to return ill-formed Unicode strings if the input contains any lone surrogates:

JSON.stringify('\uD800');
// → '"�"'

The “well-formed JSON.stringify” proposal changes JSON.stringify so it outputs escape sequences for lone surrogates, making its output valid Unicode (and representable in UTF-8):

JavaScript modules

· 20 min read
Addy Osmani ([@addyosmani](https://twitter.com/addyosmani)) and Mathias Bynens ([@mathias](https://twitter.com/mathias))

JavaScript modules are now supported in all major browsers!

This article explains how to use JS modules, how to deploy them responsibly, and how the Chrome team is working to make modules even better in the future.

What are JS modules?

JS modules (also known as “ES modules” or “ECMAScript modules”) are a major new feature, or rather a collection of new features. You may have used a userland JavaScript module system in the past. Maybe you used CommonJS like in Node.js, or maybe AMD, or maybe something else. All of these module systems have one thing in common: they allow you to import and export stuff.