DEV Community

Cover image for When changes are too expensive: JavaScript and data change performance.
Dmitry Shatokhin
Dmitry Shatokhin

Posted on

When changes are too expensive: JavaScript and data change performance.

Approaches to data processing and manipulation remain one of the key aspects of software development.
Immunability, reactivity, and mutability are key strategies in data state management, and choosing between them can significantly affect application performance.,

In JavaScript, the importance of choosing an approach to data processing is closely related to the features of this language and the scope of its application. JavaScript is primarily used to develop the client side of web applications, where performance is a key factor. The user experience directly depends on how quickly and efficiently the application processes and displays data.

Immunability in JavaScript can be quite expensive. Every time the state changes, a new object is created, which leads to increased memory usage and load on the garbage collector. In a language with dynamic typing and automatic memory management, such operations can affect performance significantly more than, for example, in languages with static typing and manual memory management.

In front-end JavaScript, the problem of immunability is getting worse. The user interface often requires a lot of state updates, which in the case of immutability can lead to the creation of a large number of objects and, accordingly, to an increased load on the processor and memory. Such resource costs in the context of a browser, where available resources are usually limited, can lead to lower performance and a worse user experience.

Nevertheless, despite its "cost", immutability continues to be popular in the JavaScript world. One example is the Redux state management library, where immunability is used to ensure predictability and simplify tracking of state changes. Immunability can facilitate the development process by providing clear and unambiguous rules for changing the state, which can be very valuable in complex applications with a large number of interdependencies.

And what about the mutable approach? For example - MobX. This library is regarded by many as an analogue of Redux, but from the camp of a mutable approach. However, you can often hear that Redux has more performance than MobX, which contradicts what we talked about. After all, in a mutable approach, we simply change the value in memory.

For example, I wrote 2 versions of a simple React application. One using Redux, the other with MobX. In all other respects, they are completely identical. The application is a background counter that is updated 1 time per second. A button that changes color by hover. And a simulation of sending N files to the server. When sending information changes, it should be updated in the interface. Sending occurs asynchronously and starts at different times for everyone, with random file size. This application lacks a complex interface and so on, but it demonstrates well the user's need to use the application while something is being done in the background (I will provide access to each application at the end of the article).

Here is the result when sending 50 files.

upload 50 files with redux

upload 50 files with mobx

There are no differences in performance here. The main counter changes 1 time per second. The color change and text highlighting are triggered. Updating information is not lagging.

When sending 100 files, the lags are already noticeable.

upload 100 files with redux

upload 100 files with mobx

When sending 500 files, the application can no longer be used and it makes no sense to test with a larger number of files.

upload 500 files with redux

upload 500 files with mobx

If you look to measure the execution time of the update function, it turns out that Redux is really faster than MobX. But with the increase in the number of files, this difference is greatly reduced.

It turns out that mutability is no faster than immunability? What is the point of realizing that an application with MobX works faster than the same application with Redux, if both of them cannot be used?
In fact, MobX has tried to combine reactivity and mutability and performs too many calculations for this. In MobX, a state change can cause side effects in other parts of the application through mechanisms such as observables and reactions. This means that when a state changes (mutates), all parts of the system that depend on this state automatically react to these changes. In addition, the observer function imposes additional costs and in console or server JavaScript - MobX will be more productive than in the frontend.

Then how to make it better? How to make sure that our application does not lag, and we can send 500 files without harm?

Let's take into account the development environment, in our case, JavaScript. Many argue for immutability, and they have valid points. However, it is foolish to disregard the impact it has on performance in JavaScript. Therefore, it may not be the optimal solution to the problem.
And the problem is a safe state with a clear data flow.
Mutability - does not guarantee that some value in the code will be accidentally redefined, etc. And it's true. But JavaScript is a language that actively uses mutability within itself. It turns out there is a way to track changes in entities. This method is a Proxy. Proxy is used to define user behaviors on objects and allows you to create various types of traps for objects. But MobX uses a Proxy! Yes, but with reservations. Let's just say Proxy is used there more for other things than for state control.
Libraries for state management are often regarded as a place where you need to shove all the business logic. From this, the vector of the library's purpose shifts from the original one - state management.

Before proceeding to the details of what should be taken into account in the state management library in the first place, let's look at another example of an application we know, but with a different library. Statemanjs.

50 files.

upload 50 files with statemanjs

100 files.

upload 100 files with statemanjs

500 files.

upload 500 files with statemanjs

Even 1000 files without lags.

upload 1000 files with statemanjs

And only when sending 5000 files there are lags, and then because of React. Nevertheless, even with lags, the application can still be used, unlike Redux and MobX when sending 500 files.

upload 5000 files with statemanjs

Is this some kind of lite version of MobX? So it's even less secure and functional?
This is not a lite version of MobX and with MobX, statemanjs - binds only Proxy. But Proxy is used here in a radically different way. More native to the language itself. Providing monitoring and security of the condition.
Statemanjs uses the concept of references for reading, and you can change the state only with built-in methods. This makes it predictable, secure and, as we have seen, faster.
I will not talk in detail about statemanjs. You can read the official documentation - Link, there are also benchmarks and comparison with other state management libraries. In this article, I wanted to draw your attention to how important it is to understand the design of the development environment. Data structures and data modification methods.

Thank you all and have a good coding!

redux codesanbox
mobx codesanbox
statemanjs codesanbox

Top comments (1)

nduyvu1511 profile image

Great post, thanks