<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged rxjs</title>
<link>https://coderlegion.com/tag/rxjs</link>
<description>Powered by Question2Answer</description>
<item>
<title>Improve your Angular app performance by using this simple Observable cache decorator 
</title>
<link>https://coderlegion.com/29028/improve-your-angular-app-performance-by-using-this-simple-observable-cache-decorator</link>
<description>&lt;p&gt;When we were about to finish development of our applications in SwiftViews we noticed a pattern in all our data-fetching user flows. In spite of the fact that the apps are all data-driven and looked really dynamic, what really was changing in the same user session wasn’t that much, but we were making http requests for new content regardless.&lt;br&gt;
The easiest solution was caching&lt;br&gt;
Caching where? On the server? We already have that, but this doesn’t stop all our apps hitting our services and thus — increasing the load on them.&lt;br&gt;
Maybe we could use service worker since it allows caching API calls?&lt;br&gt;
Yes, that was one of the options since it already has a pretty nice integration with Angular and allowed for a simple solution to selectively cache resources and APIs. However, what we wanted is to be able to not only choose what to cache, but also when to do it.&lt;br&gt;
Just to give you a quick example — we basically wanted to cache all API call results for the page below, but only if we were certain that the data source did not change in some way.&lt;br&gt;
&lt;img src=&quot;https://thepracticaldev.s3.amazonaws.com/i/x5df5ibqm2u4nqndgtz1.png&quot; alt=&quot;Inventory&quot;&gt;&lt;/p&gt;
&lt;p&gt;Since this is my personal inventory page, the only way it can actually change is if I add an item from this platform (currently the only way to do so), so I have the information about when this page will change and how long I can return cached content for.&lt;br&gt;
We figured the best way to apply this caching in a selective and configurable way will be to use what the platform already gives us&lt;br&gt;
Since we are using Angular and Typescript ❤️, and all our data calls go through RxJs, we figured that we can create a Cache Observable decorator, which we’d use to only give the caching power to certain methods.&lt;br&gt;
So, before, this was our method which called our server to get the products on the page above:&lt;/p&gt;
&lt;p&gt;and that became:&lt;/p&gt;
&lt;p&gt;Notice that the @Cacheable() has been applied just to the method we wanted and was also passed cacheBusterObserver which is basically our Subject-based mechanism to tell this exact decorated method to relieve all its caches, when any value is emitted in that stream.&lt;br&gt;
For example, the method below will “cache-bust” the caches of the method above, if the Observable it returns emits a value &lt;/p&gt;
&lt;p&gt;This might be the most complex example we have but we also have other configurations implemented like maxCacheCount, maxAge, slidingExpiration and custom cache deciders and resolvers which give us a fine-grain control on what, when and how to cache. See those in the README file.&lt;br&gt;
In the two gifs below, you can see the difference between our app’s performance without and with caching.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.loom.com/share/71a03e4cd478407fa53f131fc112e09a&quot;&gt;https://www.loom.com/share/71a03e4cd478407fa53f131fc112e09a&lt;/a&gt;&lt;br&gt;
No Cacheable&lt;br&gt;
The cacheable decorator is not applied yet, so every page load will actually fetch data from the server&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.loom.com/share/380a1fa93a9c42d0a5adaa987cd51efb&quot;&gt;https://www.loom.com/share/380a1fa93a9c42d0a5adaa987cd51efb&lt;/a&gt;&lt;br&gt;
Cacheable decorator applied &lt;br&gt;
The cacheable decorator is applied and we can see that all consecutive page loads are faster. Also the user profile page loads instantly, because we have already called the /user/{id}endpoint on the inventory page. Also, the cache busts after we add a new item so our user gets fresh data&lt;br&gt;
So, to sum up, this simple decorator allowed us to:&lt;br&gt;
Selectively cache observable methods (not just endpoints, but maybe also computation-heavy calculations in streams)&lt;br&gt;
Improve the performance of our app, without any business logic refactoring (thanks TS decorators ❤️)&lt;br&gt;
Greatly reduce the load on our servers&lt;br&gt;
If you want to use this decorator in your project, just install it from NPM!&lt;br&gt;
npm install ngx-cacheable&lt;br&gt;
If you have any questions about it or want to contribute, don’t mind opening a pull request @github or commenting below.&lt;br&gt;
Also, if you liked the application, please register and build your own inventory @ swiftviews.&lt;br&gt;
Thank you! &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29028/improve-your-angular-app-performance-by-using-this-simple-observable-cache-decorator</guid>
<pubDate>Tue, 29 Sep 2026 10:53:16 +0000</pubDate>
</item>
<item>
<title>Deep Dive: Solving Race Conditions and Concurrency with RxJS Operators</title>
<link>https://coderlegion.com/7766/deep-dive-solving-race-conditions-and-concurrency-with-rxjs-operators</link>
<description>&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=6157093721072778817&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Race conditions are the nightmares of frontend development. They happen when your code assumes that network request A will finish before network request B, but the internet decides otherwise.&lt;/p&gt;
&lt;p&gt;In React or Vue (without RxJS), managing complex concurrency often requires boolean flags (&lt;code&gt;isLoading&lt;/code&gt;), timestamp checks, or &lt;code&gt;AbortControllers&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In Angular, we solve this elegantly with &lt;strong&gt;RxJS Mapping Operators&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But RxJS gives us four different tools: &lt;code&gt;mergeMap&lt;/code&gt;, &lt;code&gt;switchMap&lt;/code&gt;, &lt;code&gt;concatMap&lt;/code&gt;, and &lt;code&gt;exhaustMap&lt;/code&gt;. Choosing the wrong one isn't just a style choice; it determines whether your app creates bugs or prevents them.&lt;/p&gt;
&lt;p&gt;Let's break down exactly which concurrency strategy each operator uses.&lt;/p&gt;
&lt;h4 id=&quot;1-switchmap-the-latest-and-greatest-fixing-read-races-&quot;&gt;1. &lt;code&gt;switchMap&lt;/code&gt;: The &quot;Latest and Greatest&quot; (Fixing Read Races)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;The Strategy:&lt;/strong&gt; &lt;em&gt;Cancel the previous request if a new one arrives.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Scenario:&lt;/strong&gt;&lt;br&gt;
Imagine a user is on a &quot;User Details&quot; page.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They click &quot;User A&quot;. (Request A starts - Slow Network).&lt;/li&gt;
&lt;li&gt;They get bored and click &quot;User B&quot; immediately. (Request B starts - Fast Network).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;The Bug (without switchMap):&lt;/strong&gt;&lt;br&gt;
Request B finishes first and shows User B. Then, Request A finishes and &lt;em&gt;overwrites&lt;/em&gt; the screen with User A. The URL says &quot;User B&quot;, but the screen shows &quot;User A&quot;. This is a dirty UI state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;this.route.params.pipe(
  // When params change, cancel any pending request!
  switchMap(params =&amp;gt; this.userService.getUser(params.id))
).subscribe(user =&amp;gt; {
  // We are GUARANTEED that 'user' matches the current URL.
  this.displayUser(user);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; GET requests (Search, Route Params, Filters).&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-concatmap-the-single-file-line-fixing-write-races-&quot;&gt;2. &lt;code&gt;concatMap&lt;/code&gt;: The &quot;Single File Line&quot; (Fixing Write Races)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;The Strategy:&lt;/strong&gt; &lt;em&gt;Wait for the current request to finish before starting the next.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Scenario:&lt;/strong&gt;&lt;br&gt;
An &quot;Auto-Save&quot; feature in a text editor.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User types &quot;Hello&quot;. (Request A sent).&lt;/li&gt;
&lt;li&gt;User immediately types &quot; World&quot;. (Request B sent).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;The Bug (with switchMap):&lt;/strong&gt;&lt;br&gt;
If you used &lt;code&gt;switchMap&lt;/code&gt;, Request B would cancel Request A. The server only receives &quot; World&quot;. The word &quot;Hello&quot; is lost forever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
We need a Queue. Request B must wait in line.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;this.textInput.valueChanges.pipe(
  // Queue the requests. Request B waits for Request A to complete.
  concatMap(text =&amp;gt; this.api.saveDraft(text))
).subscribe();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; PUT/POST requests where &lt;strong&gt;order matters&lt;/strong&gt; (Auto-saves, sequential steps).&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;3-mergemap-the-free-for-all-parallel-execution-&quot;&gt;3. &lt;code&gt;mergeMap&lt;/code&gt;: The &quot;Free-for-All&quot; (Parallel Execution)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;The Strategy:&lt;/strong&gt; &lt;em&gt;Run everything immediately. I don't care about order.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Scenario:&lt;/strong&gt;&lt;br&gt;
A &quot;Bulk Delete&quot; feature. The user selects 5 items from a list and clicks &quot;Delete Selected&quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Strategy:&lt;/strong&gt;&lt;br&gt;
We don't want to wait for Item 1 to delete before starting Item 2 (too slow). And we certainly don't want to cancel Item 1 if Item 2 starts (bug). We want them all to fly to the server at once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const idsToDelete = [1, 2, 3, 4, 5];

from(idsToDelete).pipe(
  // Fire 5 network requests in parallel
  mergeMap(id =&amp;gt; this.api.deleteItem(id))
).subscribe(deletedId =&amp;gt; {
  console.log(`Deleted ${deletedId}`);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; DELETE requests or fetching unrelated data (e.g., getting Widget A and Widget B at the same time).&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;4-exhaustmap-the-do-not-disturb-spam-prevention-&quot;&gt;4. &lt;code&gt;exhaustMap&lt;/code&gt;: The &quot;Do Not Disturb&quot; (Spam Prevention)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;The Strategy:&lt;/strong&gt; &lt;em&gt;Ignore new requests while I am busy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Scenario:&lt;/strong&gt;&lt;br&gt;
A &quot;Pay Now&quot; button or a Login form.&lt;br&gt;
A nervous user clicks the button 10 times in one second because the spinner didn't appear instantly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Bug (with mergeMap):&lt;/strong&gt;&lt;br&gt;
You charge the user's credit card 10 times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Bug (with switchMap):&lt;/strong&gt;&lt;br&gt;
You cancel the payment 9 times, and only the 10th one works (maybe).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
We want to completely ignore clicks 2 through 10.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;fromEvent(this.payButton, 'click').pipe(
  // If a payment is processing, IGNORE this click.
  exhaustMap(() =&amp;gt; this.paymentService.chargeCard())
).subscribe();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Non-idempotent POST requests (Login, Payment, Submit).&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;summary-cheat-sheet&quot;&gt;Summary Cheat Sheet&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;Operator &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Concurrency Strategy &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Ideal Use Case&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;switchMap&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Kill the previous &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Reads (Search, Routing)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;concatMap&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Wait in line &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Ordered Writes (Save)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;mergeMap&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Run in parallel &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Parallel Writes (Delete)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;exhaustMap&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Ignore the new &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Submit Buttons (Auth)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;RxJS operators are tools for managing &lt;strong&gt;time&lt;/strong&gt;. Master these four, and you master concurrency.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/7766/deep-dive-solving-race-conditions-and-concurrency-with-rxjs-operators</guid>
<pubDate>Tue, 25 Nov 2025 05:39:27 +0000</pubDate>
</item>
</channel>
</rss>