Skip to content

A Breakdown of RxJS 7.0 Performance Improvements

A Breakdown of RxJS 7.0 Performance Improvements

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

It’s official.

RxJS 7.0 has been deemed stable and is now available in its beta form.

The core team, committed to maintaining a dependable and adaptable reactive programming tool, has built this new release to improve many aspects of developer experience without triggering changing breaks to existing code.

Through a partnership with Google, the RxJS core team was able to test its latest release by integrating the library into the Google 3 monorepository. In doing so, they ran RxJs against thousands of build targets, and across countless applications, to identify where code breaks were happening, and what could be done to ensure stronger stability.

Updated Typing

RxJS 7.0 is written in Typescript 3.8, which allows users to leverage all of Typescript’s latest features. For example, RxJS’ typing inference capacity, especially with respect to how the library handles generics, has significantly improved. Previous versions struggled to type larger rests of arguments, with prior upper bound limits not usually exceeding 8 or 9 values. These limitations, however, have been removed, allowing developers to pass an infinite number of values with one call.

The team was also able to fix many antiquated types that did not reflect the runtime behavior of actual APIs. As a result, several Observables, including 'toPromise()', have been deprecated in order to mitigate the challenges of legacy behavior without significantly impacting existing code.

New Features

animationFrames(): an Observable that returns the milliseconds elapsed since the start of the Observable, which is especially useful for coding animation, and replaces previous methods of incrementing numbers.

lastValueFrom() and firstValueFrom(): Observables that return the last and first values in a promise, respectfully. They improve upon ‘toPromise()’ by returning an ‘EmptyError’ when passing promises without values, and giving users more flexibility when selecting the values they want returned.

AsyncIterable support: Any areas of code that previously accepted a Promise, or an Observable, now also accept AsyncIterables.

rxjs-for-await: a separate library which supports async-await for-await loops via AsyncIterables.

'retry({resetOnSuccess: true})': In 6.0, if you had a source that errored with, for example, a retry count valued at 5, it would only error a total of 5 times, no matter how many successful messages it had passed. With 'retry({resetOnSuccess: true})', the retry count value will reset every time a successful message is passed.

TimeStamp: an interface with a ‘now()’ method that returns a number.

Name Changes: In order to create fewer collisions between four legacy operators, and creation methods with the same name...

'zip(other$)' is now 'zipWith(other$)'. 'combineLatest(a$, b$)' is now 'combineLatestWith(a$, b$)'. 'merge(partner$)' is now 'mergeWith(partner$)'. 'concat(ender$)' is now 'concatWith(ender$)'.

Deprecations

In 7.0, no critical elements have been removed from the library, but several features have been deprecated. These include:

ResultSelectors, which have been deprecated since 5.0.

The process of passing scheduler arguments through functions such as ‘of()’ or ‘range()’ has been deprecated, and should be replaced by using ‘scheduled()’ or ‘observeOn()’. This has been done in order to remove scheduling logic from operators that don’t need it.

Some subscription signatures will be deprecated. With this newest release, subscriptions should only be called with a single function, or a partial or complete Observable. The same will also go for tap signatures. This will simplify the development process and help eventually make the library more lean with future releases.

Functions that pull in schedulers are now deprecated as of this release in order to prepare users for the eventual phase-out of passing schedulers to certain functions.

Looking Forward to 7.1 and Beyond

In order to support future changes to the library, the RxJS team will begin introducing ESLint transformations. These updates will help developers remove deprecated code, such as APIs, from their applications, and prepare users for future, major releases as the library becomes faster and leaner. However, Ben Lesh, RxJS core team lead, ensures that no breaking changes will be scheduled until version 9.0 at the earliest.

How You Can Contribute

The RxJS core team welcomes enthusiastic RxJS experts to help contribute to refining version 7’s future updates. Those experienced with ESLint transformations, or who want to update documentation are welcome to contribute to this open-source library.

And of course, the RxJS team relies on the amazing work of authors, bloggers, content creators, and those working in developer relations, to share insights into the library with the development community.

If you’re just getting started with RxJS, check out this fantastic “1 Hour to Learn RxJS” tutorial by Ben Lesh from This Dot Labs' Javascript Marathon, where you can learn about Observables and operators, including how to build an Observable from scratch!

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

What's the Latest in RxJS News? RxJS 7.5 Release Updates cover image

What's the Latest in RxJS News? RxJS 7.5 Release Updates

Are you ready? The latest updates to RxJS 7.5 boast some exciting new features. Major highlights include changes to the the RxJS roadmap. Now RxJS will be broken down to smaller packages #6786 in order to give the team the ability to publish smaller independent RxJS packages. Through this change, for example, developers will import libraries like observables as @rxjs/observables instead of rxjs/observables. This will give library authors and developers the opportunity to only import the package required for their projects. It will encourage more community contributions to separate packages, and also reduce the build size for RxJS. Other updates for RxJS 7.5 Patch Release RxJS 7.5 is the latest release, which added a number of new features that have also improved performance metrics. New features in RxJS 7.5 retry and repeat APIs allow developers to add a delay configuration to these operators (#6640 and #6421), which simplify similar operators retryWhen and repeatWhen. With these improvements, retryWhen and repeatWhen will be deprecated and removed in coming major versions #6859. The share operator was extended to allow developers to pass an observable factory to control the reset behavior, and enable reset delays, #6169. Documentation improvements RxJS 8 has now seen an alpha release, which you can check out by reviewing the official roadmap. RxJS is moving to use NX monorepo for the doc site, and RxJS main builds #6786. Other proposals include Standalone packages like the Observables package. The Standalone Observable package will unify observables usage with the existing patterns in other libraries. This will promote adoption for the proposed native observable with TC39, for which the RxJS team has been advocating. Chrome Dev Tools The Chrome Dev Tools team is discussing whether to add debugging tooling that can help developers debug features in RxJS. Curious about more updates to RxJS? Make sure to check out the Github repo and follow the changelog for more information....

Introducing @this-dot/rxidb cover image

Introducing @this-dot/rxidb

When we are working on PWAs (Progressive Web Applications), we sometimes need to implement features that require us to store data on our user's machine. One way to do that is to use IndexedDb. Using the IndexedDb browser API has its challenges, so our team at This Dot has developed an RxJS wrapper library around it. With @this-dot/rxidb, one can set up reactive database connections and manipulate its contents the RxJS way. It also provides the ability to subscribe to changes in the database and update our UIs accordingly. In this blog post, I'd like to show you some small examples of the library in action. If you'd like to see the finished examples, please check out the following links: - Auto Increment Object Store example. - Key-value pair Object Store example You can also check our OSS repository for more examples. Storing data in chronological order Imagine that you are working on a special text editor app, or something that needs to keep data between page reloads. These kinds of apps usually need to track larger amounts of data. It would be a bad practice to use localStorage for that. In the following example, we will focus on how to store and delete rows in an Object Store that has autoIncrement enabled. For the sake of simplicity, every time the user presses the Add Item button, a timestamp of the event will be stored in the database. We would also like to be able to remove items from the beginning and the end of this store. We will add two buttons to our UI to deal with that, and we would like them to be disabled if there are no entries in the store. We have a starter HTML that looks like the following: ` Initiating the Database For us to be able to store data in IndexedDb, we need a database connection and an Object Store set up. We want this Object Store to automatically increment, so we don't need to manually keep track of the last key. We do want to listen to every update that happens in the database, so let's set up our listeners for the keys and the key-value pairs using the entries() and the keys() operators. ` We want to display the contents of the database when they get updated in the #container div. For that, we need to subscribe to our keyValues$ observable. Whenever it emits, we want to update our div. ` Manipulating the data We have three buttons on our UI. One for adding data to the Object Store, and two for removing data from it. Let's set up our event emitters, using the fromEvent observable creator function from RxJS. ` We can use the addItem operator to add rows to an automatically incrementing Ojbect Store. When the Add Item button gets clicked, we want to save a timestamp into our database. ` Removing elements from the database will happen on the other two button clicks. We need the keys$ observable so we can delete the first or the last items in the store. ` Toggling button states The last feature we want to implement is toggling the remove buttons' disabled state. If there are no entries in the database we disable the buttons. If there are entries, we enable them. We can easily listen to the keyValues$ with the tap operator. ` Real-world use cases for autoIncrement Object Stores An automatically incrementing object store could be useful when your app needs to support offline mode, but you also need to log certain events happening on the UI to an API endpoint. Such audit logs must be stored locally and sent when the device comes online the next time. When the device goes offline, every outgoing request to our logging endpoint can instead put the data into this Object Store, and when the device comes online, we just read the events and send them with their timestamps. Storing objects Have you ever needed to fill out an extremely long form online? Maybe even that form was part of a wizard? It is a very bad user experience when you accidentally refresh or close the tab, and you need to start over. Of course, it could be implemented to store the unfinished form in a database somehow, but that would mean storing people's sensitive PII (Personal Identifiable Information) data. IndexedDb can help here as well because it stores that data on the user's machine. In the following example, we are going to focus on how to store data in specific keys. For the sake of simplicity, we set up some listeners and automatically save the information entered into the form. We will also have two buttons, one for clearing the form, and the other for submitting. Our HTML template looks like the following: ` Based on the above template, we do have a shape of how the object that we would like to store would look like. Let's set up a type for that, and some default constants. ` Set up the Object Store and the event listeners Setting up the database is done similarly as in the previous example. We open a connection to the IndexedDb and then create a store. But this time, we just create a default store. This way, we have full control over the keys. With this form, we want to write the value of the USER_INFO key in this Object Store. We also want to get notified when this value changes, so we set up the suerInfo$ observable using the read() operator. ` To be able to write values into our Object Store and update the data on our UI, we need some HTML elements. We set up constants that point towards our form, the two buttons, and all of the inputs inside the form. ` And finally, we set up some event listener Observables to be able to act when an event occurs. Again, we use the fromEvent creator function from rxjs. ` Set up some helper methods Before we set up our subscriptions, let's think through what behaviour we want with this form and the buttons. It is certain that we need a way to get the current value of the form that matches the UserFormValue type. We also want to set the input fields of the form, especially when we reload the page and there is data saved in our Object Store. If there is no value provided to this setter method, it should use our predefined EMPTY_FORM_VALUE constant. ` The UI should block the user from certain interactions. The submit button should be disabled while the form is invalid, and while a database write operation is still in progress. For handling the submit button state, we need two helper methods. ` Now we have every tool that we need to implement the logic. Setting up our subscriptions We would like to write the form data into the Object Store as soon as the form changes, but we don't want to start such operations on _every_ change. To mitigate this, we are going to use the debounceTime(1000) operator, so it waits for 1 second before starting the write operation. We use our getUSerFormValue() helper method to get the actual data from the input fields and we use the setItem() method on the store$ observable, inside a switchMap operator to write the values. We also want to disable the Submit button when the form changes and re-enable it if the form is valid and the write operation is finished. ` We also want to set the values of the input fields, for example, when we refresh the page. We also handled the submit button state and we set the values only if there are values to set. We use our setInputFieldValues() method to update the UI. ` When we submit the form, we will probably want to do something asynchronously. When that succeeds, we want to clear our Object Store, so we don't keep the submitted data on our user's machine. We also want to update the UI and clear the input fields. In this example, our form would send a POST request when we press the submit button. Therefore, we call event.preventDefault() on the submit event, so we stay on the page. ` And when we want to clear the form, we need to do the same with the data stored in our Object Store. ` Real-world use cases for Key-Value pair Object Stores Having persistent forms between page refreshes is just one useful feature you can do with IndexedDb. Our example above is very simple, but you can have a multi-page form. One could store the progress on such forms and allow the user to continue with the forms later. Keeping the constraints of IndexedDb in mind, one very cool feature is storing data for offline use....

Blog placeholder image

Announcing GraphQL Enterprise Connect - a Remote 1 day GraphQL Enterprise Focused Conference

Are you using GraphQL? Join us in this 1 day conference on August 14, 2020 from 9am - 5pm PDT focused on *GraphQL in the Enterprise*. Featuring some of GraphQL’s best speakers and developers on panels and sharing real world experiences on GraphQL implementation, this event is the first GraphQL event focused on problems real companies face during adoption! This event is hosted by PayPal, Braintree, and This Dot. Check out the schedule below (PDT): 9:00am - 9:30am - *Keynote Panel * 9:0045am - 10:15am - *"Lessons from the Trenches : a retrospective on our first public GraphQL API"*, Rahul Dighe 1030am - 1100am - *“Building a GraphQL Platform ”*, Sam Parsons & Kyle DeTella 11:15am - 11:45am - *“GraphQL Security”*, Joey Nenni 11:45am - 1:00pm - Lunch break 1:00pm - 1:45pm - *Community Panel * 2:00pm - 2:30pm - *“Building a faster Checkout experience w/ GraphQL at PayPal”*, Vishakha Singh 2:45pm - 3:15pm - *"Persisted GraphQL"*, Brian Douglas 3:30pm - 4:00pm - *"Data federation with GraphQL for the enterprise"*, Allison Kunz 415pm - 5:00pm - *“Pushing your React + GraphQL Testing Further”*, Sergey Mykhailenko *Join us here!*...

Next.js + MongoDB Connection Storming cover image

Next.js + MongoDB Connection Storming

Building a Next.js application connected to MongoDB can feel like a match made in heaven. MongoDB stores all of its data as JSON objects, which don’t require transformation into JavaScript objects like relational SQL data does. However, when deploying your application to a serverless production environment such as Vercel, it is crucial to manage your database connections properly. If you encounter errors like these, you may be experiencing Connection Storming: * MongoServerSelectionError: connect ECONNREFUSED <IP_ADDRESS>:<PORT> * MongoNetworkError: failed to connect to server [<hostname>:<port>] on first connect * MongoTimeoutError: Server selection timed out after <x> ms * MongoTopologyClosedError: Topology is closed, please connect * Mongo Atlas: Connections % of configured limit has gone above 80 Connection storming occurs when your application has to mount a connection to Mongo for every serverless function or API endpoint call. Vercel executes your application’s code in a highly concurrent and isolated fashion. So, if you create new database connections on each request, your app might quickly exceed the connection limit of your database. We can leverage Vercel’s fluid compute model to keep our database connection objects warm across function invocations. Traditional serverless architecture was designed for quick, stateless web app transactions. Now, especially with the rise of LLM-oriented applications built with Next.js, interactions with applications are becoming more sequential. We just need to ensure that we assign our MongoDB connection to a global variable. Protip: Use global variables Vercel’s fluid compute model means all memory, including global constants like a MongoDB client, stays initialized between requests as long as the instance remains active. By assigning your MongoDB client to a global constant, you avoid redundant setup work and reduce the overhead of cold starts. This enables a more efficient approach to reusing connections for your application’s MongoDB client. The example below demonstrates how to retrieve an array of users from the users collection in MongoDB and either return them through an API request to /api/users or render them as an HTML list at the /users route. To support this, we initialize a global clientPromise variable that maintains the MongoDB connection across warm serverless executions, avoiding re-initialization on every request. ` Using this database connection in your API route code is easy: ` You can also use this database connection in your server-side rendered React components. ` In serverless environments like Vercel, managing database connections efficiently is key to avoiding connection storming. By reusing global variables and understanding the serverless execution model, you can ensure your Next.js app remains stable and performant....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co