In this page, we will be reviewing some topics regarding use of the RxJS library. If you're not familiar with this library, it provides functionality for working with the publisher/subscriber pattern via Observables, Subjects, and functional pipes. If you would like to learn more about the core concepts, please visit our Introduction to Observables with RxJS.
- Using a state management framework to intercept the query with an effect.
- Upon each invokation, manually checking some local value to see if its defined in the consumer, then calling the load function if not from the service.
- Using some framework or custom implementation to decorate the function.
The approaches above, and others, can be valid if done appropriately, but there are many instances of these concepts being applied incorrectly, causing the codebase to be much less clean than it would have been otherwise, which is the opposite of the intended goal. The approach we will discuss is a cross between decorating the method which loads the data and caching it.
Lazy Loading within an Observable
- The network call and caching is encapsulated in one place.
- Consumers do not need to know anything about how the data is loaded or whether or not it is loaded when it subscribes.
- The implementation is concise.
- It is simple to add additional logic for caching rules, such as duration and expiry.
Adding Caching Clearing and Rules
Clearing this cache is as simple as setting the behavior subject's value back to null. A public method can be exposed to allow consumers to force a refresh.
- setting timers to call the clearCache method after some interval.
- adding an additional pipe to the behavior subject to clear the cache based on some response value.
- listening to some event to clear the cache.
The following is an example of a method to clear the cache based on the backend response:
Preventing Duplicate Invocations
- Create some logic within the observable getter property to check if a backend call is already in progress. If so, don't make another one. This is a relatively simple approach but will require each consumer to maintain their own state.
- If you have access to the service itself, you can create some logic there, similar to the point above. This will not require consumers to maintain their own state.
- Decorate the service call method/function with a function which will collect subsequent invocations, hold them, and return the result from the first call to all invocations.
In our case for this website, we generally take the last approach by using a decorator from our @byte-this/funscript library. This is a lighweight library we've created and use ourselves which provides implementations for common functional patterns. An example implementation using this library is as follows:
The article below introduces our library with greater detail: