<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged deep-dive</title>
<link>https://coderlegion.com/tag/deep-dive</link>
<description>Powered by Question2Answer</description>
<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>