I recently co-hosted an episode of the Modern Web Podcast along with Tracy Lee, in which we interviewed Ben Lesh, the author of RxJS, on the most recent updates for RxJS 7 and its targeted release date.
It was an opportunity to understand how RxJS has improved over time, and how TypeScript helped to identify bugs.
It's worth mentioning that RxJS 7 has been around a year now. The v7 alpha was released a year ago, and the first beta came out 7 months ago. Let's discover the upcoming features and breaking changes.
Better TypeScript typings
This is the biggest improvement for this latest version. Of course, this required some breaking changes. However, the team is proud to provide better type inference now.
toPromise Operator
Let's see an example with the .toPromise
Operator for a better understanding:
const values = ['a', 'b', 'c', 'd', undefined];
const result: string = await from(values).toPromise();
console.log(result); // prints 'undefined'
The Promise API ensures a result(fulfilled) or an error(rejected). However, with .toPromise
operator it's possible to get undefined
if the observable completes without emitting any value, or if that value was emitted last, and it won't throw any error.
With RxJS 7, you'll need to specify undefined
along with the union type of TypeScript:
const values = ['a', 'b', 'c', 'd', undefined];
const result: string | undefined = await from(values).toPromise(); // Promise<T | undefined>
console.log(result); // prints 'undefined'
An important note: the .toPromise
operator will be deprecated in RxJS 7 and it will be removed by RxJS 8. In its place, you'll find firstValueFrom()
and lastValueFrom()
.
firstValueFrom
This function subscribes to an Observable, and converts it to a promise. The promise result will be resolved as soon as the first value arrives from the observable, and then the subscription will be closed.
const values = ["a", "b", "c", "d"];
const observable = from(values);
const result: string = await firstValueFrom(observable); // Promise<string>
console.log(result); // prints 'a'
As consequence, this Operator should be used when you know the Observable will emit at least one value.
lastValueFrom
This function converts an Observable to a promise by subscribing to the Observable and waiting for it to complete. The Promise will resolve the last value from the stream.
const values = ["a", "b", "c", "d"];
const observable = from(values);
const result: string = await lastValueFrom(observable);
console.log(result); // prints 'd'
Of course, this Operator should be used only when you know the Observable will complete any time. Otherwise, you end up with a Promise that is hung up.
RxJS is smaller
Not too many words are needed to describe this awesome improvement. However, in the last couple of months of work, the RxJS team did a lot of code refactoring that reduced the size drastically. According to Ben Lesh, it's now almost 50% smaller.
A lot of these improvements were done through code refactoring. The original code was written 6 years ago and there have been about four rewrites that Ben created with experimental branches, but they never ever made it over. Most of these changes were sent as a pull request: refactor: smallify all the things.
RxJS supports the latest TypeScript version
The RxJS team was waiting for TypeScript 4 for code refactoring and now that v4.1 is coming, there are a couple of features they may want to use. Mostly for better type inference, and bad types fixing.
Stability Matters
The RxJS team is focused on getting those changes as stable as possible. This has a high priority considering the 23+ Million downloads the library receives every week.
On other hand, Google syncs their entire mono repository with the master branch of RxJS. Thus, RxJS 7 has been used in production for a while by Google products.
How TypeScript improved RxJS?
It's an interesting fact to know that the RxJS project is using TypeScript actively, in the words of Ben Lesh:
TypeScript is awesome. When RxJS migrated from JavaScript to TypeScript, we found bugs immediately (it was in TypeScript 1.8). RxJS implementation is using all of the stricter settings for TypeScript, We're trying to be strict as possible and I recommend that too by the way.
If you're building an App that uses libraries, TypeScript would be a great addition for you (talking about the Developer Experience), and your project. You should put in strict settings, and get everything to work.
It is worth mentioning that the TypeScript team has added a lot of features because of Angular and RxJS projects too.
RxJS 7 and Beyond
It looks like, at this point, version 8 will not need to be a complete rewrite. RxJS 8 will just go through the pending deprecations and finally get rid of them, mainly because a lot of them were sitting there for about 2 years. Of course, these changes would reduce the size of the library a lot more and will make it smaller and faster.
What about RxJS 9 or even future versions?
Ben explained that some libraries and APIs started using the AbortSignal interface. It can be used currently with fetch as a cancellation primitive:
const controller = new AbortController();
const signal = controller.signal;
function fetchResource() {
fetch(url, {signal}).then((response) => {
// process the response here
}).catch((e) => {
console.log('Error', e);
});
}
function cancel() {
controller.abort();
}
This API exists in the browser, and will be available in Node.js. RxJS would probably want to use the same cancellation mechanism. However, this is speculation for now because nothing like that will be done very quickly.
RxJS is an Open Source project
There is a lot of exciting news around RxJS. So, why is it actually taking some time to have v7 ready?
Ben explained that there's a couple of issues that need to be solved before the release of the stable version.
Also, it is difficult to set a fixed date for the expected release since these improvements and fixes are all done through volunteer work.
If you are interested in contributing with your TypeScript skills, please come and help the RxJS team!