- race lets you track multiple promises and take action as soon as the first promise either succeeds or fails.
- all only succeeds if all the promises succeed.
- allSettled returns an array listing whether each promise succeeds or fails.
- any fills a gap by succeeding as soon as any of the promises succeeds and failing if they all fail.
“The subtlety between these four is whether you need all of [the promises] or is one of them OK, and is a rejection fatal or should we keep going,” Palmer said.
Promise.any is most useful when you have a situation that offers some redundancy, he suggested. “Imagine you could fetch a document or a resource from multiple different websites. Maybe you want to accelerate the performance of your website by having it, in parallel, request it from multiple different servers and you don’t care which one of those requests succeeds. All you’re interested in is the result as soon as the first successful one completes.”
Promise.any won’t automatically terminate any of the other requests; you’re still responsible for manually canceling them after Promise.any returns its results.
If all the promises are rejected, Promise.any returns an aggregate error: an array of the individual errors from each promise, because you may need to know precisely why each operation failed.
“Promise.allSettled was the first case where we had a built-in API that could return you multiple errors and we had to figure out how to represent them, but this has been a problem for libraries for a really long time,” he said. “If you have a multistage process like trying to upload files in parallel and some of the files fail, what do you do with the errors? Well, you invent your own aggregator error.”
A future version of ECMAScript might include adding causes to the Error constructor (currently a stage three proposal) — linking a chain of errors so you can follow them back to handle the underlying issue, which will work with aggregate error.
“A lot of people probably just don’t even realize it and ship their code using replace, assume it’s going to replace all instances — and then it doesn’t,” Terlson said. “Using a regular expression might be nontrivial for certain strings. It’s pretty easy for Latin characters. But if you’ve got parens or other regex syntax in there, you can’t just replace your quotes with slashes. Now people who aren’t regexp wizards can finally replace like they wanted to all along.”
Logical Assignment Operators
“This is both a syntactic sugar for something that’s super common, plus it avoids the footgun with where someone passes you a but valid value like false, 0, or the empty string, but you end up overwriting it with the default,” Terlson said. “If you’re using some kind of observable framework you might end up doing a whole bunch of work that you don’t need to do because you didn’t change the value.”
“If the left-hand side is truthy, we never even evaluate the right-hand side,” he said. “So when you use compound assignment, we will skip the step altogether. With the nullish coalescing operator, we can start assigning missing values.” An object property might or might not have a value; now developers can write with object property ??= and give it some kind of default value. The assignment will only happen if the object property was already empty.
That’s different from compound mathematical operators, but it’s what most people would want to do in this situation. Making it the default syntax means developers are less likely to make mistakes.
It’s much easier to make sure you’re reading a long number correctly when it’s split into logical chunks. Thanks to the introduction of BigInt in ECMAScript 2020, developers can now handle very large integers. Numeric separators are syntactic sugar to make those long numbers easier to read by breaking them up with an underscore, so you can type 1_000_000_000 rather than 1000000000 for 1,000,000,000. Underscore is already used this way in Java, Python, Perl, Ruby, Rust, Julia, Ada, and C#.
“These underscores have no semantic meaning; they are purely there for us to make our numbers beautiful,” Palmer said.
Does every site need its own list of translated currencies, language, or region names to use in dropdowns for language, regions, and script pickers? Intl.DisplayNames provides standard translations for some commonly used (and translated) strings, like language names and days of the week.
“At some point, you’re going to need a drop-down for users to select language and it would be nice to be able to do that without having to manually create the translations for each of those languages, since we have the database on the computer that has the local name to the local language,” Terlson said.
WeakRef and FinalizationRegistry
If you want to save memory by dropping data you no longer need to store, you have to explicitly remove strong references, like event listeners. Even the existing WeakMap and WeakSet constructs aren’t truly weak references: The new WeakRef is a true weak reference that you can use to wrap things like event listeners, so they can be garbage collected.
WeakRef solves that, although at the cost of extra work: “Whenever you want to use the object, maybe to dereference it, you have to ask it, ‘Are you still there?’ You may find that it’s no longer there, if everyone else has released strong references to it.”
This also helps with handling related resources like subscriptions; having the subscription callbacks use weak references to poll the object means the subscriptions won’t accidentally keep the primary object alive when it’s no longer required.
Useful as it is, there are some strong caveats about using weak references if there is any other way to solve your problem because it can mean developers are writing code that’s no longer portable.
Palmer called it a power tool, and a feature of last resort: “This is a very advanced language feature that needs to be learned and understood because otherwise, you could cause problems for yourself.”
That’s where finalizers come in: Rather than polling the object, developers can register a callback to receive an event when an object is garbage collected, so they can remove the resources backing it.
“This means you shouldn’t do anything in your finalizers that can be done any other way,” Palmer warned. “If you save user data only in response to a finalizer running, you might find out that you never save that user data. This whole proposal is trying to help you do fine-grained, efficient memory recovery and clean up to make sure your program is more efficient. This is an optimization that you would layer on: this is not a place to do fundamental functionality for your app.”
Terlson noted that every time he’s thought he needed weak references, he actually didn’t, but he’s still in favor of WeakRef because of the increasing importance of WebAssembly.