<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Younes Jaaidi</title>
    <description>The latest articles on Forem by Younes Jaaidi (@younesjd).</description>
    <link>https://forem.com/younesjd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F187417%2F92275dcf-6dcf-451a-9027-2f7611483e41.jpg</url>
      <title>Forem: Younes Jaaidi</title>
      <link>https://forem.com/younesjd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/younesjd"/>
    <language>en</language>
    <item>
      <title>Vitest's 4.1 New "Fast-Forward" Mode Skips Timer Delays Instantly</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Wed, 18 Mar 2026 13:42:46 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/vitests-41-new-fast-forward-mode-skips-timer-delays-instantly-4a4h</link>
      <guid>https://forem.com/playfulprogramming-angular/vitests-41-new-fast-forward-mode-skips-timer-delays-instantly-4a4h</guid>
      <description>&lt;p&gt;An important property of tests is that they should be &lt;a href="https://cookbook.marmicode.io/angular/testing/glossary?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward#composable" rel="noopener noreferrer"&gt;composable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example. Say you have a search component with a 300ms debounce. You've already tested the debounce behavior itself — "does it wait 300ms before firing?" — in a dedicated test. Now every other test that involves this component shouldn't care whether the debounce is there or not. Change the delay from 300ms to 500ms? Only the debounce test should break. Not the 15 other tests that just happen to type into that search field.&lt;/p&gt;

&lt;p&gt;Let's look at what this problem looks like, then at different approaches to solving it.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Challenge with Manual Fake Timers
&lt;/h1&gt;

&lt;p&gt;The classic approach of fake timers in manual mode breaks composability by coupling all the tests to the debounce.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;mountCookbookSearch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Keywords&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Marmicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTimeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;310&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Angular Testing Cookbook | Marmicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test knows about the 300ms debounce &lt;em&gt;(note the 310ms — &lt;a href="https://cookbook.marmicode.io/angular/testing/controlling-time-in-tests?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward#time-is-not-a-precise-science" rel="noopener noreferrer"&gt;time is not a precise science&lt;/a&gt; 😉)&lt;/em&gt;. Change the debounce to 500ms and this test breaks — even though it's testing search results, not timing.&lt;br&gt;
You could use &lt;code&gt;vi.runAllTimersAsync()&lt;/code&gt; instead to flush all pending timers without specifying a duration. That's less coupled to the exact value, but the test still knows it needs to deal with timers at all. And manual mode freezes all timers — including framework internals like Angular's synchronization or React's scheduler fallbacks — which can require deep knowledge of framework internals just to get your test to run.&lt;/p&gt;

&lt;p&gt;Let's fix this.&lt;/p&gt;
&lt;h1&gt;
  
  
  Solution #1 — Real Time and Polling
&lt;/h1&gt;

&lt;p&gt;If you're using &lt;a href="https://cookbook.marmicode.io/angular/testing/vitest-browser-mode?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward" rel="noopener noreferrer"&gt;Vitest Browser Mode&lt;/a&gt;, DOM interactions using the &lt;code&gt;page&lt;/code&gt; API and assertions such as &lt;code&gt;expect.element&lt;/code&gt; poll. They respectively retry until the element is found or the assertion passes — or until the timeout is reached.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;mountCookbookSearch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Keywords&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Marmicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// This will keep retrying until the debounce hits and the&lt;/span&gt;
&lt;span class="c1"&gt;// "Angular Testing Cookbook" is the only cookbook remaining.&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Angular Testing Cookbook | Marmicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No fake timers. No manual time advancement. The test just waits for the heading to appear.&lt;/p&gt;

&lt;p&gt;This works, and it's composable but it has a cost: &lt;strong&gt;the test is as slow as the debounce&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution #2 — Dynamic Timing Configuration
&lt;/h1&gt;

&lt;p&gt;Another approach is to make the delay configurable and override it in tests. &lt;/p&gt;

&lt;p&gt;This is composable and fast. It's often the best approach when you control the code. I cover this pattern in detail in my &lt;a href="https://cookbook.marmicode.io/angular/testing/controlling-time-in-tests?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward" rel="noopener noreferrer"&gt;Controlling Time in Tests&lt;/a&gt; cookbook chapter.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution #3 — "Fast-Forward" Mode
&lt;/h1&gt;

&lt;p&gt;Vitest 4.1 introduces a way to control how fake timers automatically advance time &lt;code&gt;vi.setTimerTickMode('nextTimerAsync')&lt;/code&gt; — what I call "fast-forward" mode.&lt;br&gt;
Unlike manual fake timers where you have to call &lt;code&gt;vi.advanceTimersByTimeAsync(310)&lt;/code&gt; and know the delay, "fast-forward" mode advances the clock on its own, as quickly possible and as far as necessary. You don't need to know the delay value. You don't need to manually advance anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setTimerTickMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nextTimerAsync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 👈&lt;/span&gt;

&lt;span class="nf"&gt;mountCookbookSearch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Keywords&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Marmicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Angular Testing Cookbook | Marmicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 300ms debounce is skipped instantly. Change it to 5 seconds — the test still passes in milliseconds. ✨&lt;/p&gt;

&lt;p&gt;It's composable (the test doesn't know about the delay), and it's fast (no real waiting).&lt;/p&gt;

&lt;p&gt;⚠️ Make sure you &lt;a href="https://cookbook.marmicode.io/angular/testing/controlling-time-in-tests?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward#restoring-real-timers" rel="noopener noreferrer"&gt;restore real timers&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Special thanks to Andrew and Vladimir
&lt;/h1&gt;

&lt;p&gt;"Fast-forward" mode exists thanks to &lt;a href="https://github.com/atscott" rel="noopener noreferrer"&gt;Andrew Scott&lt;/a&gt; from the Angular team — alongside &lt;a href="https://github.com/sheremet-va" rel="noopener noreferrer"&gt;Vladimir Sheremet&lt;/a&gt; from the Vitest team.&lt;/p&gt;

&lt;h1&gt;
  
  
  Going Deeper
&lt;/h1&gt;

&lt;p&gt;I cover the full decision tree — manual mode, "fast-forward" mode, dynamic timing configuration, and their trade-offs — in the &lt;a href="https://cookbook.marmicode.io/angular/testing/controlling-time-in-tests?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward" rel="noopener noreferrer"&gt;Controlling Time in Tests&lt;/a&gt; chapter of my Angular Testing Cookbook. Step-by-step recipes included. 👨🏻‍🍳&lt;/p&gt;




&lt;p&gt;I'm Younes Jaaidi, Angular GDE &amp;amp; Nx Champion. I help teams write tests that survive refactoring through a &lt;a href="https://courses.marmicode.io/courses/pragmatic-angular-testing?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward" rel="noopener noreferrer"&gt;video course&lt;/a&gt;, a &lt;a href="https://marmicode.io/workshops/pragmatic-angular-testing?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward" rel="noopener noreferrer"&gt;3-day hands-on workshop&lt;/a&gt;, and a &lt;a href="https://cookbook.marmicode.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=fast-forward" rel="noopener noreferrer"&gt;free cookbook&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>testing</category>
      <category>angular</category>
    </item>
    <item>
      <title>The Missing Ingredient for Angular Template Code Coverage and Future-Proof Testing</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Mon, 18 Nov 2024 15:37:53 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/the-missing-ingredient-for-angular-template-code-coverage-and-future-proof-testing-nam</link>
      <guid>https://forem.com/playfulprogramming-angular/the-missing-ingredient-for-angular-template-code-coverage-and-future-proof-testing-nam</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Turn on &lt;a href="https://angular.dev/tools/cli/aot-compiler" rel="noopener noreferrer"&gt;Ahead-Of-Time (AOT)&lt;/a&gt; compilation for your Angular tests to get accurate template code coverage, faster test execution, production-symmetry, and future-proof tests.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The option is already available for Vitest users and will soon be available for Karma and Jest (experimental builder) users.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  😧 What's wrong with JIT?
&lt;/h1&gt;

&lt;p&gt;Whether you are using Karma, Jest, or Vitest, you are probably using Just-In-Time (JIT) compilation for your Angular tests, as &lt;strong&gt;until recently, it was the only available option&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem is that JIT has a few significant downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code coverage is not accurate&lt;/strong&gt; as the template is not taken into account.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;slower&lt;/strong&gt; as the tests compile the templates on the fly.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;not future-proof&lt;/strong&gt; as Angular has reached the limits of JIT compatibility. By design, some features are simply impossible to implement with JIT.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;not symmetrical with the production environment&lt;/strong&gt;, where AOT is used.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ⏰ Why now?
&lt;/h1&gt;

&lt;p&gt;Since Angular 8 and the introduction of &lt;a href="https://blog.angular.dev/version-9-of-angular-now-available-project-ivy-has-arrived-23c97b63cfa3" rel="noopener noreferrer"&gt;IVy&lt;/a&gt;, the Angular compiler has started transforming templates into instructions. Among many other benefits, this also meant that code coverage tools could map these instructions to the template and compute the code coverage accordingly.&lt;/p&gt;

&lt;p&gt;Theoretically, it has been possible to produce code coverage by running tests with AOT since Angular 8, but the option was not available in Karma or Jest. &lt;strong&gt;It has only been possible to enable AOT for testing since Vitest support for Angular was added by the &lt;a href="https://analogjs.org/docs/contributors" rel="noopener noreferrer"&gt;Analog team&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As of November 2024:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vitest is the only option that supports AOT compilation.&lt;/li&gt;
&lt;li&gt;There are open PRs for &lt;a href="https://github.com/angular/angular-cli/pull/28854" rel="noopener noreferrer"&gt;Karma&lt;/a&gt; and &lt;a href="https://github.com/angular/angular-cli/pull/28855" rel="noopener noreferrer"&gt;Jest Experimental Builder&lt;/a&gt; to support AOT.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/12wPymHTbfpQtN5uPSm5NH/2c8dd99febc50808570090c0804a3181/angular-template-coverage-1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/12wPymHTbfpQtN5uPSm5NH/2c8dd99febc50808570090c0804a3181/angular-template-coverage-1.png" alt="Angular Template Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  🎁 Other Benefits of AOT Testing
&lt;/h1&gt;

&lt;h2&gt;
  
  
  ⚡️ Faster Test Execution
&lt;/h2&gt;

&lt;p&gt;Whether you are using JIT or AOT, components will end up being compiled at some point. The main difference is that with AOT, the compilation is done once and can be cached, while with JIT, each test module might end up recompiling components.&lt;/p&gt;

&lt;p&gt;This means that even if the transform phase is a bit slower with AOT, the overall test execution time will be faster. The numbers I've seen indicate &lt;strong&gt;around 20% faster execution&lt;/strong&gt;, but this will depend heavily on the structure of your tests and the System Under Test.&lt;/p&gt;

&lt;h2&gt;
  
  
  👯 Production-Symmetry
&lt;/h2&gt;

&lt;p&gt;We generally want our tests to be as symmetric as possible to the production environment to increase confidence. This is often challenging as it balances against other properties like the speed of the tests, the size of the System Under Test, or predictability.&lt;/p&gt;

&lt;p&gt;The interesting aspect of AOT is that it improves production-symmetry without harming other properties. &lt;strong&gt;Using AOT, you will gain more confidence and achieve behavior that is closer to production.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 Future-Proof Tests
&lt;/h2&gt;

&lt;p&gt;More importantly, JIT has reached its limits and is becoming a liability for Angular. For instance, some Angular features are simply not supported in JIT (e.g. &lt;a href="https://angular.dev/guide/defer" rel="noopener noreferrer"&gt;Deferrable Views&lt;/a&gt;). Other potential features from the Angular roadmap, like selectorless components, will &lt;strong&gt;probably&lt;/strong&gt; be impossible to use with JIT.&lt;/p&gt;

&lt;p&gt;Actually, since Angular's Signal Inputs &lt;em&gt;(and similar functional APIs)&lt;/em&gt;, JIT already requires some minimal transforms to work.&lt;/p&gt;

&lt;p&gt;By switching to AOT, you are making your tests future-proof, ready to benefit from any innovation, and &lt;strong&gt;prepared for whatever the future holds for JIT.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  🤔 Drawbacks
&lt;/h1&gt;

&lt;h2&gt;
  
  
  🪄 Dynamic Component Constructs Should Be Avoided
&lt;/h2&gt;

&lt;p&gt;By turning on AOT, some techniques that rely on dynamic constructs will break.&lt;/p&gt;

&lt;p&gt;For instance, such usage will not work anymore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 🛑 This is broken with AOT.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;app-button/&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;imports&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestContainer&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TestContainer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, bypassing the AOT compilation is still possible &lt;em&gt;(⚠️ for now ️⚠️)&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;imports&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestContainer&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TestContainer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My advice is to &lt;strong&gt;avoid such constructs as much as possible and prefer creating test-specific components when needed&lt;/strong&gt;, even though it might be a bit more verbose. In the future, the Angular team could provide alternatives that are both AOT-compatible and less boilerplat-y.&lt;/p&gt;

&lt;h2&gt;
  
  
  🦦 Shallow Testing is More Challenging
&lt;/h2&gt;

&lt;p&gt;While Shallow Testing should not be your primary testing strategy as it is also less production-symmetric, it is still a useful technique to have in your toolbox.&lt;/p&gt;

&lt;p&gt;With AOT, it is currently impossible to override a component's imports using &lt;code&gt;TestBed#overrideComponent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The workaround is to override the component's dependencies at the module level using the testing framework's API and replace components with their test doubles.&lt;/p&gt;

&lt;p&gt;For example, with Vitest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.cmp.spec.ts&lt;/span&gt;
&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./street-map.cmp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;StreetMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./street-map-fake.cmp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StreetMapFake&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// street-map-fake.cmp.ts&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-street-map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fake Street Map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StreetMapFake&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;StreetMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this temporary workaround is AOT-compatible, it comes with a cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is less readable and more verbose.&lt;/li&gt;
&lt;li&gt;"Mocking" &lt;em&gt;(or providing test doubles)&lt;/em&gt; at the module level is less granular and can be less predictable.&lt;/li&gt;
&lt;li&gt;It is highly coupled to the testing framework you are using.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, I would recommend using JIT for Shallow Tests until &lt;code&gt;TestBed#overrideComponent&lt;/code&gt; supports AOT or until the Angular team provides a better alternative. You can achieve this by using a separate configuration for Shallow Tests that use JIT for tests matching a specific pattern like &lt;code&gt;*.jit.spec.ts&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  👨🏻‍🍳 Taking Vitest with AOT for a Spin
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Set up Vitest
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For Angular CLI users, use &lt;a href="https://analogjs.org/fr/docs/features/testing/vitest" rel="noopener noreferrer"&gt;Analog's schematic&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For Nx users, choose the &lt;code&gt;vitest&lt;/code&gt; option when generating an application or library (available since Nx 20.1.0).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Enable AOT
&lt;/h2&gt;

&lt;p&gt;Locate the &lt;code&gt;vite.config.js&lt;/code&gt; file and &lt;a&gt;turn on AOT by setting Angular's plugin &lt;code&gt;jit&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;angular&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  📈 Configure Code Coverage
&lt;/h1&gt;

&lt;p&gt;We have the option to use either &lt;code&gt;istanbul&lt;/code&gt; or native &lt;code&gt;v8&lt;/code&gt; for code coverage. For some reason, still under investigation, Vitest coverage remapping fails when using v8. The solution is to fall back to using &lt;code&gt;istanbul&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install &lt;code&gt;@vitest/coverage-istanbul&lt;/code&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure you install the Vitest Istanbul version that matches &lt;strong&gt;Vitest's major version&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @vitest/coverage-istanbul
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Choose &lt;code&gt;istanbul&lt;/code&gt; as your coverage provider
&lt;/h2&gt;

&lt;p&gt;Update the &lt;code&gt;vite.config.mts&lt;/code&gt; to &lt;a&gt;enable coverage&lt;/a&gt; using Istanbul:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;coverage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;istanbul&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🎬 Watch it in Action
&lt;/h1&gt;

&lt;p&gt;You can now run the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nx &lt;span class="nb"&gt;test &lt;/span&gt;my-app &lt;span class="nt"&gt;--coverage&lt;/span&gt; &lt;span class="nt"&gt;--ui&lt;/span&gt; &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
ng &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--coverage&lt;/span&gt; &lt;span class="nt"&gt;--ui&lt;/span&gt; &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then click on the coverage icon and admire the template's code coverage. 🤯&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/4JYgUy4Nn0kFlCUH1PDvBd/d3c5b00378f78d65991c43ffdae7f09e/vitest-coverage-button.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/4JYgUy4Nn0kFlCUH1PDvBd/d3c5b00378f78d65991c43ffdae7f09e/vitest-coverage-button.png" alt="vitest-coverage-button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(you will also find the coverage report in the &lt;code&gt;coverage&lt;/code&gt; folder)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/12wPymHTbfpQtN5uPSm5NH/2c8dd99febc50808570090c0804a3181/angular-template-coverage-1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/12wPymHTbfpQtN5uPSm5NH/2c8dd99febc50808570090c0804a3181/angular-template-coverage-1.png" alt="Angular Template Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that the coverage is computed based on the instructions generated by the compiler, which means that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It will even work for structural directives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/7mTVtOBPHxlN2mZS1W5gxm/0cbe4afe0e318887b200eb9816f5fa25/angular-tempalte-coverage-2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/7mTVtOBPHxlN2mZS1W5gxm/0cbe4afe0e318887b200eb9816f5fa25/angular-tempalte-coverage-2.png" alt="Angular Structural Directive Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, guess what!?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The coverage also works for inline templates! 🚀&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/5q2z4mkPhH7GPJZNnlcJwW/16889b4a104111d6d04e088527deac2f/angular-template-coverage-3.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/5q2z4mkPhH7GPJZNnlcJwW/16889b4a104111d6d04e088527deac2f/angular-template-coverage-3.png" alt="Angular Inline Template Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  ☢️ Mind the Code Coverage Trap
&lt;/h1&gt;

&lt;p&gt;While code coverage is a useful tool, it should be used wisely. Keep it as an indicator, not a rigid goal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Any observed statistical regularity will tend to collapse once pressure is placed upon it for control purposes.&lt;/p&gt;

&lt;p&gt;-- Charles Goodhart&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, when a measure becomes a target, it ceases to be a good measure.&lt;/p&gt;

&lt;p&gt;I'd add that &lt;strong&gt;the simplest metrics are often the most misleading&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🚀 What's Next?
&lt;/h1&gt;

&lt;p&gt;Karma users will soon be able to enable AOT with a simple flag.&lt;/p&gt;

&lt;p&gt;Jest users have three options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recommended&lt;/strong&gt;: Migrate to Vitest. &lt;em&gt;(📻 stay tuned as I'll soon be sharing the smoothest migration path)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Use the experimental builder with AOT.&lt;/li&gt;
&lt;li&gt;Wait for &lt;code&gt;jest-preset-angular&lt;/code&gt; AOT support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vitest users can enjoy the benefits of AOT right now. 🎉&lt;/p&gt;

&lt;h1&gt;
  
  
  📚 Additional Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;💻 &lt;a href="https://github.com/marmicode/cookbook-demos/tree/angular-testing-aot" rel="noopener noreferrer"&gt;Demo Repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📝 &lt;a href="https://angular.dev/tools/cli/aot-compiler" rel="noopener noreferrer"&gt;Angular Ahead-Of-Time (AOT) Compiler Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📝 &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📝 &lt;a href="https://analogjs.org/docs/features/testing/vitest" rel="noopener noreferrer"&gt;Analog's Docs on Vitest&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  👨🏻‍🏫 Pragmatic Angular Testing Video Course is Here!
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://courses.marmicode.io/courses/pragmatic-angular-testing" rel="noopener noreferrer"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/67D0wLRVxtTwDqW4F0WGBT/a69bdd1253876914381b051cbbc34b21/pragmatic-angular-testing-course-screenshot.png" alt="pragmatic-angular-testing-course-screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are tired of bugs, or high-maintenance tests that break at each refactor, my video course, &lt;a href="https://courses.marmicode.io/courses/pragmatic-angular-testing" rel="noopener noreferrer"&gt;Pragmatic Angular Testing&lt;/a&gt;, is here to help!&lt;/p&gt;

&lt;p&gt;Learn practical, reliable testing strategies to keep your Angular apps stable and maintainable. &lt;em&gt;(Now 50% off for a limited time!)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>testing</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Managing RxJS Traffic with Signals and Suspensify</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Mon, 10 Jul 2023 12:31:20 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/managing-rxjs-traffic-with-signals-and-suspensify-pp</link>
      <guid>https://forem.com/playfulprogramming-angular/managing-rxjs-traffic-with-signals-and-suspensify-pp</guid>
      <description>&lt;h1&gt;
  
  
  Will Signals replace RxJS?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;TL;DR: No! Not totally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Angular is not the first framework to implement Signals, it is The framework where developers rely most on RxJS. This raises many interrogations on the future of RxJS in the Angular ecosystem and whether Signals will partially or totally replace RxJS.&lt;/p&gt;

&lt;p&gt;What we are facing here is what I like to call the &lt;strong&gt;Spoon-and-Fork situation&lt;/strong&gt; which we are used to in the software world.&lt;/p&gt;

&lt;p&gt;Considering that spoons probably appeared before forks, the day forks appeared, some might have seen them as a replacement for spoons. When they tried them on mashed potatoes... until the day they were served soup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each time two solutions overlap in solving some specific problem, the last one released can be hastily and mistakenly considered as a total replacement of the older one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;RxJS provides unequaled ways of managing time-related manipulations of push-based data streams like buffering, throttling, retrying, exponential backoff, or orchestration through flattening strategies.&lt;/strong&gt;&lt;br&gt;
On the other hand, it is not necessarily the best fit for describing the reactive graph between a value that we change and the Angular views using it.&lt;/p&gt;

&lt;p&gt;That is where Signals come into play and simplify the propagation of changes to the views.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to learn more about Signals, I highly recommend &lt;a href="https://angularexperts.io/blog/angular-signals-push-pull" rel="noopener noreferrer"&gt;this blog post by my friend Tomas Trajan&lt;/a&gt;, and also &lt;a href="https://marmicode.io/blog/angular-signals-and-custom-render-strategies" rel="noopener noreferrer"&gt;my own blog post 😅 if you want a quick deep dive in some internals&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;RxJS has a time dimension which is very interesting but it comes with a cost, the cost of using RxJS operators &lt;em&gt;(i.e. map)&lt;/em&gt; and Angular pipes &lt;em&gt;(i.e. &lt;code&gt;async&lt;/code&gt; &amp;amp; &lt;code&gt;push&lt;/code&gt;)&lt;/em&gt; to “push” the changes to the view.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signals on the other hand don’t have any stream-awareness or time-awareness but they are pretty good at building a reactive graph and propagating changes from the value change to the view and through the transformations made by "computed" signals.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  ✨ Let Signals shine!
&lt;/h1&gt;

&lt;p&gt;While there is clearly some overlap between Signals and RxJS, let’s make each one of them shine where it fits best.&lt;br&gt;
Unless you don’t care about the download/upload progress, retry capabilities, and anything time-related, you might not need RxJS, but most of the time we'll want to care about these things... except at the view level.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What matters most at the component/view level is the current state. A component lives in the present, and Signals are a representation of the present.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's just bridge the gap and "project" the observable's present state to a Signal using &lt;code&gt;toSignal()&lt;/code&gt; function, and let's go out for a beer, right?&lt;/p&gt;

&lt;p&gt;But wait!&lt;/p&gt;

&lt;p&gt;What happens if there is an error?&lt;/p&gt;

&lt;p&gt;Also, how do we know if the observable emitted a value or not yet?&lt;/p&gt;

&lt;p&gt;And what about an observable that emits multiple values, how can we know if it did complete or not?&lt;/p&gt;
&lt;h1&gt;
  
  
  ♻️ Spinner vertigo
&lt;/h1&gt;

&lt;p&gt;Suppose that we are fetching data from a remote service. Let’s &lt;a&gt;convert the Observable to a Signal&lt;/a&gt; and &lt;a&gt;forward it to a child component&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;mc-recipe [recipe]=”recipe()”/&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCmp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🍔&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  That’s when we might hit a common typing issue:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error TS2322: Type 'string | undefined' is not assignable to type 'string'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The child component is expecting a &lt;code&gt;string&lt;/code&gt; as an input but the current default behavior of &lt;code&gt;toSignal()&lt;/code&gt; is to not require the Observable to emit a value before the Signal is read. It falls back on the default initial value which is &lt;code&gt;undefined&lt;/code&gt; and that can be customized using the &lt;code&gt;initialValue&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;So, we can either force people to eat pizza and empty strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🍕&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or stick to the default behavior…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…then handle this in the template and show a spinner until the data is ready.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;mc&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;spinner&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ngIf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;!recipe()&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;mc&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ngIf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipe() as recipeValue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipeValue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, what if for some chaotic reason, the Observable returned by &lt;code&gt;getRecipe()&lt;/code&gt; emits a &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt; value or an empty string?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In that case, the spinner would just keep spinning because we can’t make any difference between the pending status and an emitted value that matches the initial value.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This also raises, the problem of using &lt;code&gt;*ngIf + as&lt;/code&gt; to narrow the type and undergo the boolean coercion as a side effect.&lt;/p&gt;

&lt;h1&gt;
  
  
  💥 “Oups! Your valid data is invalid.”
&lt;/h1&gt;

&lt;p&gt;Errors happen and we might want to handle them 😉&lt;/p&gt;

&lt;p&gt;In fact, if our Observable errors, the Signal &lt;em&gt;(and those computed from it)&lt;/em&gt; will simply throw the error when read.&lt;br&gt;
As Signals will mostly be read in the template, there is no convenient way of handling the error.&lt;/p&gt;

&lt;p&gt;The first solution that one might think of is using RxJS’s &lt;code&gt;catchError()&lt;/code&gt; and &lt;a&gt;setting the error in another signal&lt;/a&gt; &lt;a&gt;as a side effect&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div *ngIf="error()"&amp;gt;Oups! Something went wrong.&amp;lt;/div&amp;gt;
    &amp;lt;mc-recipe *ngIf="recipe() as recipeValue" [recipe]="recipeValue" /&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this approach is that side effects will often cause inconsistencies. As we manually set the error signal, we have to also manually set it to &lt;code&gt;null&lt;/code&gt; when back on track, otherwise, here is what can happen:&lt;/p&gt;

&lt;p&gt;&lt;a href="//videos.ctfassets.net/gowvxq3b4aid/BgSjzP2xArcR5DVnUEGis/3839f53c3416c3ae6ad16b40ad4238db/rxjs-signal-error.mp4" class="article-body-image-wrapper"&gt;&lt;img src="//videos.ctfassets.net/gowvxq3b4aid/BgSjzP2xArcR5DVnUEGis/3839f53c3416c3ae6ad16b40ad4238db/rxjs-signal-error.mp4" alt="RxJS to Signal Error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error sticks there even after we move to the next recipe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In order to fix this, we can either add some additional spaghetti code… or wait a bit. Sorry for the suspense!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  ✅ We’re not done yet!
&lt;/h1&gt;

&lt;p&gt;Remember that Observables can emit multiple values! Suppose that our &lt;code&gt;getRecipe()&lt;/code&gt; method still fetches the recipe from a remote service but it might emit a first value from some cache while it fetches a fresher version of the recipe from a remote service.&lt;/p&gt;

&lt;p&gt;Even if we receive the first cached value, we might want to still show a progress bar until the stream is complete in order to let the user know that we are still trying to load some additional or fresher data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The current state is neither pending nor done but somewhere in between.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem we have here is that the Signal somehow “swallowed” the “complete” notification and we have no built-in way of knowing if the stream is finished or not.&lt;br&gt;
We have to figure out a better way than &lt;a&gt;combining the &lt;code&gt;finalize()&lt;/code&gt; operator&lt;/a&gt; &lt;a&gt;with another Signal&lt;/a&gt; just like we did with &lt;code&gt;catchError()&lt;/code&gt;. Otherwise, we would encounter the same inconsistency problems described above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;mc-progress-bar *ngIf="!finalized()"/&amp;gt;
    &amp;lt;app-recipe *ngIf="recipe() as recipeValue" [recipe]="recipeValue" /&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;finalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;finalize&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🪄 Suspensify
&lt;/h1&gt;

&lt;p&gt;We need to figure out a way of mapping Observables to a richer type of Signal that would contain a &lt;strong&gt;consistent and current projection of the Observable's state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;materialize&lt;/code&gt; operator sounds like the natural option, but it doesn’t solve our problem for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it doesn’t emit anything before the first value, error, or “complete” notification, so we have to emit in initial “started” notification,&lt;/li&gt;
&lt;li&gt;the “complete” notification doesn’t contain the last emitted value, so we have to somehow remember it (e.g. by applying a reducer using the &lt;code&gt;scan&lt;/code&gt; operator).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This “Observable projection problem” is not new or solely related to Signals. This also happens when we try to project the current status of an asynchronous task in NgRx effects or when using RxAngular’s connect method.&lt;br&gt;
That is the reason why we created the &lt;code&gt;suspensify()&lt;/code&gt; operator with my friend &lt;a href="https://twitter.com/edbzn" rel="noopener noreferrer"&gt;Edouard Bozon&lt;/a&gt; a few years ago. Cf. &lt;a href="https://github.com/jscutlery/devkit/tree/main/packages/operators" rel="noopener noreferrer"&gt;https://github.com/jscutlery/devkit/tree/main/packages/operators&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This operator produces an observable that will always have an initial value and that never throws an error.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of emitting the Observable values, it will emit a &lt;code&gt;Suspense&lt;/code&gt; object containing different properties that will tell us about the current state of the Observable.&lt;/p&gt;

&lt;p&gt;The example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;suspensify&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;could emit the following values if the Observables emits a value then fails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hasValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;finalized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hasValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🍔&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;finalized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hasValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;💥&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can turn this into a Signal that will always have an initial value and never throw.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;suspensify&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt; &lt;span class="c1"&gt;// Signal&amp;lt;Suspense&amp;lt;Recipe&amp;gt; | undefined&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we try to use this in the template like this we stumble upon an error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- error TS2532: Object is possibly 'undefined' --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;mc-progress-bar&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;”!recipe().finalized”/&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, the default initial value of a Signal is &lt;code&gt;undefined&lt;/code&gt; but we can fix the typing by letting &lt;code&gt;toSignal()&lt;/code&gt; know that our Observable will synchronously emit an initial value as we are using &lt;code&gt;suspensify()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;suspensify&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;requireSync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Signal&amp;lt;Suspense&amp;lt;Recipe&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  💪 Type-narrowing in the template
&lt;/h1&gt;

&lt;p&gt;The default behavior of &lt;code&gt;suspensify()&lt;/code&gt; is to return a type union in order to help with type narrowing.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"suspense.hasError"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ suspense.error }} // ✅
  {{ suspense.value }} // 💥 template compilation error
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"suspense.hasValue"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ suspense.error }} // 💥 template compilation error
  {{ suspense.value }} // ✅
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this will avoid some common mistakes and allow us to propagate the right type to the children, it currently doesn’t play well with Signals because the compiler doesn’t know that &lt;code&gt;recipe()&lt;/code&gt; will always return the same value during a Change Detection cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"recipe().hasError"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ recipe().error }} // 💥 template compilation error
  {{ recipe().value }} // 💥 template compilation error
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"recipe().hasValue"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ recipe().error }} // 💥 template compilation error
  {{ recipe().value }} // 💥 template compilation error
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This is currently in the Angular roadmap and will be fixed in some future version, at least for signal-based components.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meanwhile, the workaround is to set &lt;code&gt;suspensify&lt;/code&gt;’s &lt;code&gt;strict&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt; so that the &lt;code&gt;error&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; properties are always available but potentially &lt;code&gt;undefined&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;suspensify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;})));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"recipe().hasError"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ recipe().error }} // ✅
  {{ recipe().value }} // ✅
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"recipe().hasValue"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ recipe().error }} // ✅
  {{ recipe().value }} // ✅
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another alternative is to use create a local variable using &lt;code&gt;ngIf + as&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;”recipe()&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="na"&gt;suspense&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"suspense.hasError"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ suspense.error }} // ✅
    {{ suspense.value }} // 💥
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"suspense.hasValue"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ suspense.error }} // 💥
    {{ suspense.value }} // ✅
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this workaround provides better type narrowing, it is not very convenient and according to Signals RFC, this will probably not work in signal-based components 😕&lt;br&gt;
… but remember! Signal-based components will probably ship with type narrowing in the template.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finally, we can quickly wrap this in a &lt;a&gt;reusable function&lt;/a&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
&amp;lt;mc-progress-bar *ngIf=”!recipe().finalized”/&amp;gt;

&amp;lt;div *ngIf="recipe().hasError"&amp;gt;
  {{ recipe().error }}
&amp;lt;/div&amp;gt;

&amp;lt;div *ngIf="recipe().hasValue"&amp;gt;
  {{ recipe().value }}
&amp;lt;/div&amp;gt;
`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCmp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSuspenseSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nf"&gt;getRecipe&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Recipe&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;…&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toSuspenseSignal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;suspensify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;requireSync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🧳 Key Takeaways
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;🤝 Signals are not meant to replace Observables.&lt;/li&gt;
&lt;li&gt;💪 A simple operator like &lt;code&gt;suspensify()&lt;/code&gt; can keep things declarative, and thus consistent, avoiding imperative spaghetti.&lt;/li&gt;
&lt;li&gt;🐙 You can use &lt;code&gt;suspensify()&lt;/code&gt; in NgRx effects or with RxState in order to connect different sources.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  🔗 Links &amp;amp; Upcoming Workshops
&lt;/h1&gt;

&lt;p&gt;👨🏻‍🏫 &lt;a href="https://marmicode.eventbrite.com" rel="noopener noreferrer"&gt;Workshops&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📦 &lt;a href="https://github.com/jscutlery/devkit/tree/main/packages/operators" rel="noopener noreferrer"&gt;Suspensify Operator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;a href="https://stackblitz.com/edit/angular-signals-suspensify?file=src/main.ts" rel="noopener noreferrer"&gt;Source Code on Stackblitz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📰 &lt;a href="https://gmail.us3.list-manage.com/subscribe?u=915d6ba70c9c00912ba326214&amp;amp;id=71255f30c7" rel="noopener noreferrer"&gt;Subscribe to Newsletter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💬 &lt;a href="https://github.com/marmicode/marmicode/discussions/84" rel="noopener noreferrer"&gt;Discuss this on github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>rxjs</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond Angular Signals: Signals &amp; Custom Render Strategies</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Mon, 17 Apr 2023 08:35:25 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/beyond-angular-signals-signals-custom-render-strategies-46h7</link>
      <guid>https://forem.com/playfulprogramming-angular/beyond-angular-signals-signals-custom-render-strategies-46h7</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Angular Signals &lt;em&gt;might&lt;/em&gt; make it easier to track all the expressions in a view &lt;em&gt;(Component or EmbeddedView)&lt;/em&gt; and schedule custom render strategies in a very surgical way. Thus, enabling some exciting optimizations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While libraries and frameworks are getting better and better at tracking changes in a fine-grained way and propagating them to the DOM, we might notice that sometimes, &lt;strong&gt;the performance bottleneck resides in DOM updates&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's explore how Angular Signals could allow us to overcome these performance bottlenecks with custom rendering strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  📜 From Tick to Sig
&lt;/h2&gt;

&lt;p&gt;It has been a while now since the Angular team has been exploring &lt;em&gt;(way more than we can think)&lt;/em&gt; alternative reactivity models and looking for something that lies between the extremes of &lt;strong&gt;naive Zone.js&lt;/strong&gt; &lt;em&gt;(i.e. Zone.js without &lt;code&gt;OnPush&lt;/code&gt;)&lt;/em&gt; and &lt;strong&gt;Zoneless Angular&lt;/strong&gt; combined with special pipes &amp;amp; directives like those provided by &lt;a href="https://github.com/rx-angular/rx-angular" rel="noopener noreferrer"&gt;RxAngular&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;... then &lt;a href="https://twitter.com/pkozlowski_os" rel="noopener noreferrer"&gt;Pawel Kozlowski&lt;/a&gt; &lt;a href="https://twitter.com/pkozlowski_os/status/1519746420898676739" rel="noopener noreferrer"&gt;joined the Angular team as a full-time member&lt;/a&gt; and together with &lt;a href="https://twitter.com/synalx" rel="noopener noreferrer"&gt;Alex Rickabaugh&lt;/a&gt; they merged into &lt;strong&gt;Pawælex&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the meantime, while &lt;a href="https://twitter.com/RyanCarniato" rel="noopener noreferrer"&gt;Ryan Carniato&lt;/a&gt; keeps insisting that he did not invent Signals, he undoubtedly made them popular in the JavaScript ecosystem &lt;em&gt;(Cf. &lt;a href="https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob"&gt;The Evolution of Signals in JavaScript&lt;/a&gt;)&lt;/em&gt; and eventually ended up influencing Angular.&lt;/p&gt;

&lt;p&gt;That is how Pawælex &amp;amp; friends: &lt;a href="https://twitter.com/AScottAngular" rel="noopener noreferrer"&gt;Andrew&lt;/a&gt;, &lt;a href="https://twitter.com/dylhunn" rel="noopener noreferrer"&gt;Dylan&lt;/a&gt; &amp;amp; &lt;a href="https://hachyderm.io/@jelbourn" rel="noopener noreferrer"&gt;Jeremy&lt;/a&gt; made the &lt;a href="https://github.com/angular/angular/discussions/49685" rel="noopener noreferrer"&gt;Angular Signals RFC&lt;/a&gt; happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  😬 DOM updates are not that cheap
&lt;/h2&gt;

&lt;p&gt;The fantastic thing about Signals is how frameworks and libraries like Angular, SolidJS, Preact or Qwik "magically" track changes and rerender whatever has to rerender without much boilerplate compared to more manual alternatives.&lt;/p&gt;

&lt;p&gt;But wait! If they rerender whatever has to rerender, what happens if the performance bottleneck is the DOM update itself?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's try updating 10.000 elements every 100ms...&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div *ngFor="let _ of lines"&amp;gt;{{ count() }}&amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Oups! We're spending more than 90% of our time rendering...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/7MsPX3c58XqiPBv6J2fAc0/3ffc5d4240f4a8cebd5b8d3faaa58e65/flamechart-default-render-cropped.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/7MsPX3c58XqiPBv6J2fAc0/3ffc5d4240f4a8cebd5b8d3faaa58e65/flamechart-default-render-cropped.png" alt="flamechart-default-render"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...and we can notice the frame rate dropping to somewhere around 20fps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/3Y6BrW9hBoY7OGIdUptEGh/4ce5f0bf7a55f844c1456ed7af576e60/frame-rate-default-render.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/3Y6BrW9hBoY7OGIdUptEGh/4ce5f0bf7a55f844c1456ed7af576e60/frame-rate-default-render.png" alt="frame-rate-default-render"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🦧 Let's calm down a bit
&lt;/h2&gt;

&lt;p&gt;The first solution which we might think of is simply updating the Signals only when we want to rerender, but that would require some boilerplate &lt;em&gt;(i.e. creating intermediate Signals, which are not computed Signals!)&lt;/em&gt;, and this is how it would look like if we want to throttle a Signal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{{ throttledCount() }}`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCmp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;throttledCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttleSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cf. &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/signal-utils.ts" rel="noopener noreferrer"&gt;&lt;code&gt;throttleSignal()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;but this has a couple of drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐞 using a single unthrottled Signal in the same view would defeat our efforts,&lt;/li&gt;
&lt;li&gt;⏱️ if intermediate Signals scheduled updates are not coalesced, we might introduce some random inconsistencies and break the whole glitch-free implementation of Signals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📺 Updating the viewport only
&lt;/h2&gt;

&lt;p&gt;What if the browser was sensitive? It would turn to us and say: "I'm tired of working so much and nobody cares about my efforts! From now on, I won't work if you don't look at me!"&lt;/p&gt;

&lt;p&gt;We might probably agree!&lt;/p&gt;

&lt;p&gt;In fact, why would we keep updating below the fold elements? Or more generally, why would we keep updating elements outside the viewport?&lt;/p&gt;

&lt;p&gt;If we tried to implement this using an intermediate Signal, then the function would need a reference to the DOM element in order to know if it's in the viewport:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;lazyCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;applyViewportStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this would require more boilerplate and as the same Signal might be used in different places, then we would need an intermediate Signal for each usage.&lt;/p&gt;

&lt;p&gt;While this could be solved using a structural directive, we would clutter the template instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;span *lazyViewportSignal="count(); let countValue"&amp;gt;{{ countValue }}&amp;lt;/span&amp;gt;
  &amp;lt;span&amp;gt; x 2 = &amp;lt;/span&amp;gt;
  &amp;lt;span *lazyViewportSignal="double(); let doubleValue"&amp;gt;{{ doubleValue }}&amp;lt;/span&amp;gt;
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... which is far from ideal.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 What about Eventual Consistency for DOM updates?
&lt;/h2&gt;

&lt;p&gt;Another alternative is acting at the change detection level. If we can customize the rendering strategy, then we can easily postpone the rendering of the content below the fold.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More precisely, we could stop updating the content outside the viewport until it's in the viewport.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While introducing such inconsistency between the state and the view might sound frightening. If applied wisely, this is nothing more than &lt;strong&gt;Eventual Consistency&lt;/strong&gt;, meaning that we will eventually end up in a consistent state.&lt;/p&gt;

&lt;p&gt;After all, we could state the following theorem &lt;em&gt;(obviously inspired by the &lt;a href="https://en.wikipedia.org/wiki/CAP_theorem" rel="noopener noreferrer"&gt;CAP Theorem&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The process of synchronizing the state and the view can't guarantee both consistency and availability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Inspired by the work of my &lt;a href="https://github.com/rx-angular/rx-angular" rel="noopener noreferrer"&gt;RxAngular&lt;/a&gt; friends, I thought that by combining something like &lt;a href="https://www.rx-angular.io/docs/cdk/render-strategies" rel="noopener noreferrer"&gt;custom render strategies&lt;/a&gt; with the Signals tracking system, we could get the best of both worlds and achieve our goal in the most unobtrusive way.&lt;/p&gt;

&lt;p&gt;This could look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div *viewportStrategy&amp;gt;
      &amp;lt;span&amp;gt;{{ count() }}&amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt; x 2 = &amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;{{ double() }} &amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  👨🏻‍🍳 Sneaking between Signals &amp;amp; Change Detection
&lt;/h2&gt;

&lt;p&gt;Obviously, my first move was to ask the Angular team &lt;em&gt;(more precisely, my dear friend Alex who is now part of Pawælex as mentioned before)&lt;/em&gt; if there were any plans to provide an API to override how Signals trigger Change Detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alex said:&lt;/strong&gt; no.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I heard:&lt;/strong&gt; not yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then I said:&lt;/strong&gt; thanks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And we simultaneously said:&lt;/strong&gt; bye.&lt;/p&gt;

&lt;p&gt;That's when I put on my coding apron and started trying some naive stuff.&lt;/p&gt;

&lt;p&gt;My first try was nothing more than something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * This doesn't work as expected!
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Yeay! we are in!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// if called more than once&lt;/span&gt;
  &lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... but it didn't work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The naive idea behind this was that if &lt;code&gt;effect()&lt;/code&gt; can track Signal calls and if &lt;code&gt;detectChanges()&lt;/code&gt; has to synchronously call the Signals in the view, then the effect should run again each time a Signal changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's when I realized that we are lucky that this doesn't work because otherwise, this would mean that we would trigger change detection on our view whenever a Signal changes in any child or deeply nested child.&lt;/p&gt;

&lt;p&gt;Something at the view level stopped the propagation of the Signals and acted as a boundary mechanism. I had to find what it was, and the best way was to jump into the source code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Yeah! I know... I like to try random stuff first 😬)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔬 The Reactive Graph
&lt;/h2&gt;

&lt;p&gt;In order for the Signals to track changes, Angular has to build a reactive graph. Each node in this graph extends the &lt;code&gt;ReactiveNode&lt;/code&gt; abstract class.&lt;/p&gt;

&lt;p&gt;There are currently four types of reactive nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writable Signals:&lt;/strong&gt; &lt;code&gt;signal()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computed Signals:&lt;/strong&gt; &lt;code&gt;computed()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watchers:&lt;/strong&gt; &lt;code&gt;effect()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;the Reactive Logical View Consumer:&lt;/strong&gt; the special one we need 😉
&lt;em&gt;(the introduction of Signal-based components will probably add more node types like component inputs)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each &lt;code&gt;ReactiveNode&lt;/code&gt; &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L91-L107" rel="noopener noreferrer"&gt;knows all of its consumers and producers&lt;/a&gt; &lt;em&gt;(which are all &lt;code&gt;ReactiveNode&lt;/code&gt;s)&lt;/em&gt;. This is necessary in order to achieve the push/pull glitch-free implementation of Angular Signals.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/5YOk0K7Z4XVIqwLKhoZf9r/d1cfd16cf36d198656b30031df94499d/angular-signals-reactive-graph.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/5YOk0K7Z4XVIqwLKhoZf9r/d1cfd16cf36d198656b30031df94499d/angular-signals-reactive-graph.jpg" alt="angular-signals-reactive-graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This reactive graph is built using the &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L28-L32" rel="noopener noreferrer"&gt;&lt;code&gt;setActiveConsumer()&lt;/code&gt;&lt;/a&gt; function which sets the currently active consumer in a global variable which is &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L193-L220" rel="noopener noreferrer"&gt;read by the producer&lt;/a&gt; when called in the same call stack.&lt;/p&gt;

&lt;p&gt;Finally, whenever a reactive node might have changed, &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L183" rel="noopener noreferrer"&gt;it notifies its consumers&lt;/a&gt; by calling their &lt;code&gt;onConsumerDependencyMayHaveChanged()&lt;/code&gt; method. &lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 The Reactive Logical View Consumer
&lt;/h2&gt;

&lt;p&gt;While spelunking, and ruining my apron, I stumbled upon a surprising reactive node type that lives in IVy' renderer source code, the &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/render3/reactive_lview_consumer.ts" rel="noopener noreferrer"&gt;&lt;code&gt;ReactiveLViewConsumer&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While writable Signals are the leaf nodes of the reactive graph, the Reactive Logical View Consumers are the root nodes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just like any other reactive node, this one implements the &lt;code&gt;onConsumerDependencyMayHaveChanged()&lt;/code&gt; method, but not like any other reactive node, this one is bound to a view so it can control the change detection... and it does! by &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/render3/reactive_lview_consumer.ts#L32" rel="noopener noreferrer"&gt;marking the view as dirty&lt;/a&gt; when notified by a producer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;onConsumerDependencyMayHaveChanged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nf"&gt;markViewDirty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_lView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🐘 Sneaking &lt;em&gt;(like an elephant)&lt;/em&gt; between Signals &amp;amp; Change Detection
&lt;/h2&gt;

&lt;p&gt;Sadly, there doesn't seem to be any elegant way of overriding the current behavior of marking the view to check when Signals trigger a change notification...&lt;/p&gt;

&lt;p&gt;...but, luckily, I have my coding apron on, so I am not afraid of getting dirty.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create the embedded view
&lt;/h3&gt;

&lt;p&gt;First, let's create a typical structural directive so we can create &amp;amp; control the embedded view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[viewportStrategy]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ViewportStrategyDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_templateRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_vcr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_templateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Trigger change detection &lt;strong&gt;once&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For some reason, the &lt;code&gt;ReactiveLViewConsumer&lt;/code&gt; is instantiated &lt;strong&gt;after&lt;/strong&gt; the first change detection. My apron was already too dirty to dive any deeper, but my guess is that it is lazily initialized when Signals are used for performance's sake.&lt;/p&gt;

&lt;p&gt;The workaround is to trigger change detection once before detaching the change detector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aha! While writing this, I stumbled upon &lt;a href="https://github.com/angular/angular/blob/2703fd626040c5e65401ebd776404a3b9e284724/packages/core/src/render3/interfaces/view.ts#L351" rel="noopener noreferrer"&gt;this comment here&lt;/a&gt;... so I was right! Finally once! Yeah!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Grab the &lt;code&gt;ReactiveLViewConsumer&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;🙈&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reactiveViewConsumer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_lView&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;REACTIVE_TEMPLATE_CONSUMER&lt;/span&gt; &lt;span class="cm"&gt;/* 23 */&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Override the Signal notification handler like a monkey
&lt;/h3&gt;

&lt;p&gt;Now that we have the &lt;code&gt;ReactiveLViewConsumer&lt;/code&gt; instance, we can let the hacker in us override the &lt;code&gt;onConsumerDependencyMayHaveChanged()&lt;/code&gt; method and trigger/skip/schedule change detection with the strategy of our choice, like a naive throttle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;reactiveViewConsumer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onConsumerDependencyMayHaveChanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... or we can use &lt;a href="https://rxjs.dev/" rel="noopener noreferrer"&gt;RxJS&lt;/a&gt; which is still one of the most convenient ways of handling timing-related strategies &lt;em&gt;(and it is already bundled anyway in most apps 😉)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cf. &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/throttle-strategy.directive.ts" rel="noopener noreferrer"&gt;ThrottleStrategyDirective&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/viewport-strategy.directive.ts" rel="noopener noreferrer"&gt;ViewportStrategyDirective&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 and it works!
&lt;/h2&gt;

&lt;p&gt;Let's try!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/5tYvJf2tceByKoqVBLJHCO/356e910fcfbbd8e32fd4d00f06d08c0f/viewport-strategy.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/5tYvJf2tceByKoqVBLJHCO/356e910fcfbbd8e32fd4d00f06d08c0f/viewport-strategy.gif" alt="viewport-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This seems to be &lt;strong&gt;at least 5 times faster&lt;/strong&gt;... &lt;em&gt;(even though, tracking the element's appearance in the viewport is a relatively expensive task)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/1qPtr98ZnNoiWlgzzsnOoJ/905730297afbe3cf737d9584567365a6/flamechart-viewport-strategy.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/1qPtr98ZnNoiWlgzzsnOoJ/905730297afbe3cf737d9584567365a6/flamechart-viewport-strategy.png" alt="flamechart-viewport-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and the frame rate is pretty decent:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/7kYUHJ2aBCgeETKXkYWHEZ/473e3cbe60492dcba7590dd624002f43/frame-rate-viewport-strategy.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/7kYUHJ2aBCgeETKXkYWHEZ/473e3cbe60492dcba7590dd624002f43/frame-rate-viewport-strategy.png" alt="frame-rate-viewport-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... but note that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This might break in any future version &lt;em&gt;(major or minor)&lt;/em&gt; of Angular. Maybe, you shouldn't do this at work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, this only tracks the view handled by the directive. It won't detach and track child views or components. &lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 What's next?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🚦 RxAngular + Signals
&lt;/h3&gt;

&lt;p&gt;The strategies implemented in our demo are willingly naive and need better scheduling and coalescing to reduce the amount of reflows &amp;amp; repaints.&lt;br&gt;
Instead of venturing into that, this could be combined with &lt;a href="https://www.rx-angular.io/docs/cdk/render-strategies" rel="noopener noreferrer"&gt;RxAngular Render Strategies&lt;/a&gt;... wink, wink, wink! 😉 to my RxAngular friends.&lt;/p&gt;
&lt;h3&gt;
  
  
  🅰️ We might need more low-level Angular APIs
&lt;/h3&gt;

&lt;p&gt;To achieve our goal, we had to hack our way into Angular internals which might change without notice in future versions.&lt;/p&gt;

&lt;p&gt;If Angular could provide some additional APIs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ViewRef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* This doesn't exist. */&lt;/span&gt;
  &lt;span class="nf"&gt;setCustomSignalChangeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... or something less verbose 😅, we could combine this with &lt;code&gt;ViewRef.detach()&lt;/code&gt; and easily sneak in between Signals and change detection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal-Based Components
&lt;/h3&gt;

&lt;p&gt;As of today, Signal-based components are not implemented yet so there is no way to know if this would work, as implementation details will probably change.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚛ Custom Render Strategies in some other Libraries &amp;amp; Frameworks
&lt;/h2&gt;

&lt;p&gt;What about other libraries and frameworks?&lt;/p&gt;

&lt;p&gt;I couldn't refrain from asking, so &lt;a href="https://twitter.com/yjaaidi/status/1642193294900842497" rel="noopener noreferrer"&gt;I did&lt;/a&gt; and received interesting feedback from SolidJS's &lt;a href="https://twitter.com/RyanCarniato" rel="noopener noreferrer"&gt;Ryan Carniato&lt;/a&gt; &amp;amp; Preact's &lt;a href="https://twitter.com/_developit" rel="noopener noreferrer"&gt;Jason Miller&lt;/a&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  SolidJS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/RyanCarniato/status/1642956077976399872" rel="noopener noreferrer"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/1u1pnRq07aElFkwoAfjPLa/fd5ab3b48d1d3d1dfdf0879c0ec12b43/solidjs-ryan-carniato-render-strategy-cropped.png" alt="solidjs-ryan-carniato-render-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Preact
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/_developit/status/1643134288555302912" rel="noopener noreferrer"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/4AEdMShFiCni4ikJVwzdbo/e57db97e52a3d97eb24b58066e24edf2/preact-jason-miller-render-strategy-cropped.png" alt="preact-jason-miller-render-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  React
&lt;/h3&gt;

&lt;p&gt;In React, no matter if we are using Signals or not, we could implement a &lt;a&gt;Higher Order Component&lt;/a&gt; that decides whether to really render or return a memoized value depending on its strategy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterWithViewportStrategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withViewportStrategy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterWithViewportStrategy&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cf. &lt;a href="https://stackblitz.com/edit/react-custom-render-strategies?file=App.tsx" rel="noopener noreferrer"&gt;React Custom Render Strategies Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This could probably be more efficient with Signals if achieved by wrapping &lt;code&gt;React.createElement&lt;/code&gt; &lt;a href="https://github.com/preactjs/signals/blob/0a585660e141f3d92fb8789c234e69d5a1da8a86/packages/react/src/index.ts#L207" rel="noopener noreferrer"&gt;like Preact Signals integration does&lt;/a&gt; and implementing a custom strategy instead of the &lt;a href="https://github.com/preactjs/signals/blob/0a585660e141f3d92fb8789c234e69d5a1da8a86/packages/react/src/index.ts#L136" rel="noopener noreferrer"&gt;default behavior&lt;/a&gt;.&lt;br&gt;
Or maybe, using a custom hook based on &lt;a href="https://react.dev/reference/react/useSyncExternalStore" rel="noopener noreferrer"&gt;&lt;code&gt;useSyncExternalStore()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Vue.js
&lt;/h3&gt;

&lt;p&gt;Using JSX, we could wrap the &lt;code&gt;render()&lt;/code&gt; just like &lt;code&gt;withMemo()&lt;/code&gt; does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;viewportStrategy&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;rootEl&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rootEl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cf. &lt;a href="https://stackblitz.com/edit/vue-custom-render-strategy?file=src%2Fcomponents%2FCounter.jsx" rel="noopener noreferrer"&gt;throttle example on Stackblitz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... but I'm still wondering how this could work in SFC without having to add a compiler node transform to convert something like &lt;code&gt;v-viewport-strategy&lt;/code&gt; into a wrapper. 🤔&lt;/p&gt;

&lt;h3&gt;
  
  
  Qwik
&lt;/h3&gt;

&lt;p&gt;This one needs a bit more investigation 😅, and I am not sure if overriding the default render strategy is currently feasible. &lt;br&gt;
However, my first guess would be that this can be "qwikly" added to the framework.&lt;br&gt;
For example, there could be an API allowing us to toggle a component's "DETACHED" flag which would skip scheduling component render in &lt;a href="https://github.com/BuilderIO/qwik/blob/9c555682ef8e708c12c8d284aa668853888c981e/packages/qwik/src/core/render/dom/notify-render.ts#L58-L85" rel="noopener noreferrer"&gt;&lt;code&gt;notifyRender()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  👨🏻‍🏫 Closing Observations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ☢️ Please, don't do this at work!
&lt;/h3&gt;

&lt;p&gt;The presented solution is based on internal APIs that might change at any moment, including the next Angular minor or patch versions.&lt;/p&gt;

&lt;p&gt;So why write about it? My goal here is to show some new capabilities that could be enabled thanks to Signals while improving the Developer eXperience at the same time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Wanna try custom render strategies before switching to Signals?
&lt;/h3&gt;

&lt;p&gt;Check out &lt;a href="https://www.rx-angular.io/docs/template" rel="noopener noreferrer"&gt;RxAngular's template&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;While custom render strategies can instantly improve performance in some specific situations, the final note is that you should prefer &lt;strong&gt;keeping a low number of DOM elements&lt;/strong&gt; and &lt;strong&gt;reducing the number of updates&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In other words, keep your apps simple &lt;em&gt;(as much as you can)&lt;/em&gt;, organized, and your data flow  optimized by design using fine-grained reactivity &lt;em&gt;(whether you are using RxJS-based solutions, or Signals)&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔗 Links &amp;amp; Upcoming Workshops
&lt;/h2&gt;

&lt;p&gt;👨🏻‍🏫 &lt;a href="https://marmicode.eventbrite.com" rel="noopener noreferrer"&gt;Workshops&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📰 &lt;a href="https://gmail.us3.list-manage.com/subscribe?u=915d6ba70c9c00912ba326214&amp;amp;id=71255f30c7" rel="noopener noreferrer"&gt;Subscribe to Newsletter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/counter.component.ts" rel="noopener noreferrer"&gt;Source Code Repository&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;💬 &lt;a href="https://github.com/marmicode/marmicode/discussions/82" rel="noopener noreferrer"&gt;Discuss this on github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond Angular Signals: Signals &amp; Custom Render Strategies</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Fri, 07 Apr 2023 08:16:55 +0000</pubDate>
      <link>https://forem.com/marmicode/beyond-angular-signals-signals-custom-render-strategies-489j</link>
      <guid>https://forem.com/marmicode/beyond-angular-signals-signals-custom-render-strategies-489j</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Angular Signals &lt;em&gt;might&lt;/em&gt; make it easier to track all the expressions in a view &lt;em&gt;(Component or EmbeddedView)&lt;/em&gt; and schedule custom render strategies in a very surgical way. Thus, enabling some exciting optimizations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While libraries and frameworks are getting better and better at tracking changes in a fine-grained way and propagating them to the DOM, we might notice that sometimes, &lt;strong&gt;the performance bottleneck resides in DOM updates&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's explore how Angular Signals could allow us to overcome these performance bottlenecks with custom rendering strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  📜 From Tick to Sig
&lt;/h2&gt;

&lt;p&gt;It has been a while now since the Angular team has been exploring &lt;em&gt;(way more than we can think)&lt;/em&gt; alternative reactivity models and looking for something that lies between the extremes of &lt;strong&gt;naive Zone.js&lt;/strong&gt; &lt;em&gt;(i.e. Zone.js without &lt;code&gt;OnPush&lt;/code&gt;)&lt;/em&gt; and &lt;strong&gt;Zoneless Angular&lt;/strong&gt; combined with special pipes &amp;amp; directives like those provided by &lt;a href="https://github.com/rx-angular/rx-angular" rel="noopener noreferrer"&gt;RxAngular&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;... then &lt;a href="https://twitter.com/pkozlowski_os" rel="noopener noreferrer"&gt;Pawel Kozlowski&lt;/a&gt; &lt;a href="https://twitter.com/pkozlowski_os/status/1519746420898676739" rel="noopener noreferrer"&gt;joined the Angular team as a full-time member&lt;/a&gt; and together with &lt;a href="https://twitter.com/synalx" rel="noopener noreferrer"&gt;Alex Rickabaugh&lt;/a&gt; they merged into &lt;strong&gt;Pawælex&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the meantime, while &lt;a href="https://twitter.com/RyanCarniato" rel="noopener noreferrer"&gt;Ryan Carniato&lt;/a&gt; keeps insisting that he did not invent Signals, he undoubtedly made them popular in the JavaScript ecosystem &lt;em&gt;(Cf. &lt;a href="https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob"&gt;The Evolution of Signals in JavaScript&lt;/a&gt;)&lt;/em&gt; and eventually ended up influencing Angular.&lt;/p&gt;

&lt;p&gt;That is how Pawælex &amp;amp; friends: &lt;a href="https://twitter.com/AScottAngular" rel="noopener noreferrer"&gt;Andrew&lt;/a&gt;, &lt;a href="https://twitter.com/dylhunn" rel="noopener noreferrer"&gt;Dylan&lt;/a&gt; &amp;amp; &lt;a href="https://hachyderm.io/@jelbourn" rel="noopener noreferrer"&gt;Jeremy&lt;/a&gt; made the &lt;a href="https://github.com/angular/angular/discussions/49685" rel="noopener noreferrer"&gt;Angular Signals RFC&lt;/a&gt; happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  😬 DOM updates are not that cheap
&lt;/h2&gt;

&lt;p&gt;The fantastic thing about Signals is how frameworks and libraries like Angular, SolidJS, Preact or Qwik "magically" track changes and rerender whatever has to rerender without much boilerplate compared to more manual alternatives.&lt;/p&gt;

&lt;p&gt;But wait! If they rerender whatever has to rerender, what happens if the performance bottleneck is the DOM update itself?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's try updating 10.000 elements every 100ms...&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div *ngFor="let _ of lines"&amp;gt;{{ count() }}&amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Oups! We're spending more than 90% of our time rendering...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/7MsPX3c58XqiPBv6J2fAc0/3ffc5d4240f4a8cebd5b8d3faaa58e65/flamechart-default-render-cropped.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/7MsPX3c58XqiPBv6J2fAc0/3ffc5d4240f4a8cebd5b8d3faaa58e65/flamechart-default-render-cropped.png" alt="flamechart-default-render"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...and we can notice the frame rate dropping to somewhere around 20fps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/3Y6BrW9hBoY7OGIdUptEGh/4ce5f0bf7a55f844c1456ed7af576e60/frame-rate-default-render.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/3Y6BrW9hBoY7OGIdUptEGh/4ce5f0bf7a55f844c1456ed7af576e60/frame-rate-default-render.png" alt="frame-rate-default-render"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🦧 Let's calm down a bit
&lt;/h2&gt;

&lt;p&gt;The first solution which we might think of is simply updating the Signals only when we want to rerender, but that would require some boilerplate &lt;em&gt;(i.e. creating intermediate Signals, which are not computed Signals!)&lt;/em&gt;, and this is how it would look like if we want to throttle a Signal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{{ throttledCount() }}`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCmp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;throttledCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttleSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cf. &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/signal-utils.ts" rel="noopener noreferrer"&gt;&lt;code&gt;throttleSignal()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;but this has a couple of drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐞 using a single unthrottled Signal in the same view would defeat our efforts,&lt;/li&gt;
&lt;li&gt;⏱️ if intermediate Signals scheduled updates are not coalesced, we might introduce some random inconsistencies and break the whole glitch-free implementation of Signals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📺 Updating the viewport only
&lt;/h2&gt;

&lt;p&gt;What if the browser was sensitive? It would turn to us and say: "I'm tired of working so much and nobody cares about my efforts! From now on, I won't work if you don't look at me!"&lt;/p&gt;

&lt;p&gt;We might probably agree!&lt;/p&gt;

&lt;p&gt;In fact, why would we keep updating below the fold elements? Or more generally, why would we keep updating elements outside the viewport?&lt;/p&gt;

&lt;p&gt;If we tried to implement this using an intermediate Signal, then the function would need a reference to the DOM element in order to know if it's in the viewport:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;lazyCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;applyViewportStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this would require more boilerplate and as the same Signal might be used in different places, then we would need an intermediate Signal for each usage.&lt;/p&gt;

&lt;p&gt;While this could be solved using a structural directive, we would clutter the template instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;span *lazyViewportSignal="count(); let countValue"&amp;gt;{{ countValue }}&amp;lt;/span&amp;gt;
  &amp;lt;span&amp;gt; x 2 = &amp;lt;/span&amp;gt;
  &amp;lt;span *lazyViewportSignal="double(); let doubleValue"&amp;gt;{{ doubleValue }}&amp;lt;/span&amp;gt;
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... which is far from ideal.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 What about Eventual Consistency for DOM updates?
&lt;/h2&gt;

&lt;p&gt;Another alternative is acting at the change detection level. If we can customize the rendering strategy, then we can easily postpone the rendering of the content below the fold.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More precisely, we could stop updating the content outside the viewport until it's in the viewport.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While introducing such inconsistency between the state and the view might sound frightening. If applied wisely, this is nothing more than &lt;strong&gt;Eventual Consistency&lt;/strong&gt;, meaning that we will eventually end up in a consistent state.&lt;/p&gt;

&lt;p&gt;After all, we could state the following theorem &lt;em&gt;(obviously inspired by the &lt;a href="https://en.wikipedia.org/wiki/CAP_theorem" rel="noopener noreferrer"&gt;CAP Theorem&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The process of synchronizing the state and the view can't guarantee both consistency and availability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Inspired by the work of my &lt;a href="https://github.com/rx-angular/rx-angular" rel="noopener noreferrer"&gt;RxAngular&lt;/a&gt; friends, I thought that by combining something like &lt;a href="https://www.rx-angular.io/docs/cdk/render-strategies" rel="noopener noreferrer"&gt;custom render strategies&lt;/a&gt; with the Signals tracking system, we could get the best of both worlds and achieve our goal in the most unobtrusive way.&lt;/p&gt;

&lt;p&gt;This could look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div *viewportStrategy&amp;gt;
      &amp;lt;span&amp;gt;{{ count() }}&amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt; x 2 = &amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;{{ double() }} &amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  👨🏻‍🍳 Sneaking between Signals &amp;amp; Change Detection
&lt;/h2&gt;

&lt;p&gt;Obviously, my first move was to ask the Angular team &lt;em&gt;(more precisely, my dear friend Alex who is now part of Pawælex as mentioned before)&lt;/em&gt; if there were any plans to provide an API to override how Signals trigger Change Detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alex said:&lt;/strong&gt; no.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I heard:&lt;/strong&gt; not yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then I said:&lt;/strong&gt; thanks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And we simultaneously said:&lt;/strong&gt; bye.&lt;/p&gt;

&lt;p&gt;That's when I put my coding apron and started trying some naive stuff.&lt;/p&gt;

&lt;p&gt;My first try was nothing more than something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * This doesn't work as expected!
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Yeay! we are in!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// if called more than once&lt;/span&gt;
  &lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... but it didn't work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The naive idea behind this was that if &lt;code&gt;effect()&lt;/code&gt; can track Signal calls and if &lt;code&gt;detectChanges()&lt;/code&gt; has to synchronously call the Signals in the view, then the effect should run again each time a Signal changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's when I realized that we are lucky that this doesn't work because otherwise, this would mean that we would trigger change detection on our view whenever a Signal changes in any child or deeply nested child.&lt;/p&gt;

&lt;p&gt;Something at the view level stopped the propagation of the Signals and acted as a boundary mechanism. I had to find what it was, and the best way was to jump into the source code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Yeah! I know... I like to try random stuff first 😬)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔬 The Reactive Graph
&lt;/h2&gt;

&lt;p&gt;In order for the Signals to track changes, Angular has to build a reactive graph. Each node in this graph extends the &lt;code&gt;ReactiveNode&lt;/code&gt; abstract class.&lt;/p&gt;

&lt;p&gt;There are currently four types of reactive nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writable Signals:&lt;/strong&gt; &lt;code&gt;signal()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computed Signals:&lt;/strong&gt; &lt;code&gt;computed()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watchers:&lt;/strong&gt; &lt;code&gt;effect()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;the Reactive Logical View Consumer:&lt;/strong&gt; the special one we need 😉
&lt;em&gt;(the introduction of Signal-based components will probably add more node types like component inputs)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each &lt;code&gt;ReactiveNode&lt;/code&gt; &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L91-L107" rel="noopener noreferrer"&gt;knows all of its consumers and producers&lt;/a&gt; &lt;em&gt;(which are all &lt;code&gt;ReactiveNode&lt;/code&gt;s)&lt;/em&gt;. This is necessary in order to achieve the push/pull glitch-free implementation of Angular Signals.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/5YOk0K7Z4XVIqwLKhoZf9r/d1cfd16cf36d198656b30031df94499d/angular-signals-reactive-graph.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/5YOk0K7Z4XVIqwLKhoZf9r/d1cfd16cf36d198656b30031df94499d/angular-signals-reactive-graph.jpg" alt="angular-signals-reactive-graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This reactive graph is built using the &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L28-L32" rel="noopener noreferrer"&gt;&lt;code&gt;setActiveConsumer()&lt;/code&gt;&lt;/a&gt; function which sets the currently active consumer in a global variable which is &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L193-L220" rel="noopener noreferrer"&gt;read by the producer&lt;/a&gt; when called in the same call stack.&lt;/p&gt;

&lt;p&gt;Finally, whenever a reactive node might have changed, &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/signals/src/graph.ts#L183" rel="noopener noreferrer"&gt;it notifies its consumers&lt;/a&gt; by calling their &lt;code&gt;onConsumerDependencyMayHaveChanged()&lt;/code&gt; method. &lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 The Reactive Logical View Consumer
&lt;/h2&gt;

&lt;p&gt;While spelunking, and ruining my apron, I stumbled upon a surprising reactive node type that lives in IVy' renderer source code, the &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/render3/reactive_lview_consumer.ts" rel="noopener noreferrer"&gt;&lt;code&gt;ReactiveLViewConsumer&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While writable Signals are the leaf nodes of the reactive graph, the Reactive Logical View Consumers are the root nodes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just like any other reactive node, this one implements the &lt;code&gt;onConsumerDependencyMayHaveChanged()&lt;/code&gt; method, but not like any other reactive node, this one is bound to a view so it can control the change detection... and it does! by &lt;a href="https://github.com/angular/angular/blob/e9dd7f00280b6d52fa2a21da793521db1a9d7a53/packages/core/src/render3/reactive_lview_consumer.ts#L32" rel="noopener noreferrer"&gt;marking the view as dirty&lt;/a&gt; when notified by a producer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;onConsumerDependencyMayHaveChanged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nf"&gt;markViewDirty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_lView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🐘 Sneaking &lt;em&gt;(like an elephant)&lt;/em&gt; between Signals &amp;amp; Change Detection
&lt;/h2&gt;

&lt;p&gt;Sadly, there doesn't seem to be any elegant way of overriding the current behavior of marking the view to check when Signals trigger a change notification...&lt;/p&gt;

&lt;p&gt;...but, luckily, I have my coding apron on, so I am not afraid of getting dirty.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create the embedded view
&lt;/h3&gt;

&lt;p&gt;First, let's create a typical structural directive so we can create &amp;amp; control the embedded view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[viewportStrategy]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ViewportStrategyDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_templateRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_vcr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_templateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Trigger change detection &lt;strong&gt;once&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For some reason, the &lt;code&gt;ReactiveLViewConsumer&lt;/code&gt; is instantiated &lt;strong&gt;after&lt;/strong&gt; the first change detection. My apron was already too dirty to dive any deeper, but my guess is that it is lazily initialized when Signals are used for performance's sake.&lt;/p&gt;

&lt;p&gt;The workaround is to trigger change detection once before detaching the change detector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aha! While writing this, I stumbled upon &lt;a href="https://github.com/angular/angular/blob/2703fd626040c5e65401ebd776404a3b9e284724/packages/core/src/render3/interfaces/view.ts#L351" rel="noopener noreferrer"&gt;this comment here&lt;/a&gt;... so I was right! Finally once! Yeah!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Grab the &lt;code&gt;ReactiveLViewConsumer&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;🙈&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reactiveViewConsumer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_lView&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;REACTIVE_TEMPLATE_CONSUMER&lt;/span&gt; &lt;span class="cm"&gt;/* 23 */&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Override the Signal notification handler like a monkey
&lt;/h3&gt;

&lt;p&gt;Now that we have the &lt;code&gt;ReactiveLViewConsumer&lt;/code&gt; instance, we can let the hacker in us override the &lt;code&gt;onConsumerDependencyMayHaveChanged()&lt;/code&gt; method and trigger/skip/schedule change detection with the strategy of our choice, like a naive throttle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;reactiveViewConsumer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onConsumerDependencyMayHaveChanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... or we can use &lt;a href="https://rxjs.dev/" rel="noopener noreferrer"&gt;RxJS&lt;/a&gt; which is still one of the most convenient ways of handling timing-related strategies &lt;em&gt;(and it is already bundled anyway in most apps 😉)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cf. &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/throttle-strategy.directive.ts" rel="noopener noreferrer"&gt;ThrottleStrategyDirective&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/viewport-strategy.directive.ts" rel="noopener noreferrer"&gt;ViewportStrategyDirective&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 and it works!
&lt;/h2&gt;

&lt;p&gt;Let's try!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/5tYvJf2tceByKoqVBLJHCO/356e910fcfbbd8e32fd4d00f06d08c0f/viewport-strategy.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/5tYvJf2tceByKoqVBLJHCO/356e910fcfbbd8e32fd4d00f06d08c0f/viewport-strategy.gif" alt="viewport-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This seems to be &lt;strong&gt;at least 5 times faster&lt;/strong&gt;... &lt;em&gt;(even though, tracking the element appearance in the viewport is a relatively expensive task)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/1qPtr98ZnNoiWlgzzsnOoJ/905730297afbe3cf737d9584567365a6/flamechart-viewport-strategy.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/1qPtr98ZnNoiWlgzzsnOoJ/905730297afbe3cf737d9584567365a6/flamechart-viewport-strategy.png" alt="flamechart-viewport-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and the frame rate is pretty decent:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/gowvxq3b4aid/7kYUHJ2aBCgeETKXkYWHEZ/473e3cbe60492dcba7590dd624002f43/frame-rate-viewport-strategy.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/7kYUHJ2aBCgeETKXkYWHEZ/473e3cbe60492dcba7590dd624002f43/frame-rate-viewport-strategy.png" alt="frame-rate-viewport-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... but note that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This might break in any future version &lt;em&gt;(major or minor)&lt;/em&gt; of Angular. Maybe, you shouldn't do this at work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, this only tracks the view handled by the directive. It won't detach and track child views or components. &lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 What's next?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🚦 RxAngular + Signals
&lt;/h3&gt;

&lt;p&gt;The strategies implemented in our demo are willingly naive and they need better scheduling and coalescing to reduce the amount of reflows &amp;amp; repaints.&lt;br&gt;
Instead of venturing into that, this could be combined with &lt;a href="https://www.rx-angular.io/docs/cdk/render-strategies" rel="noopener noreferrer"&gt;RxAngular Render Strategies&lt;/a&gt;... wink, wink, wink! 😉 to my RxAngular friends.&lt;/p&gt;
&lt;h3&gt;
  
  
  🅰️ We might need more low-level Angular APIs
&lt;/h3&gt;

&lt;p&gt;To achieve our goal, we had to hack our way into Angular internals which might change without notice in future versions.&lt;/p&gt;

&lt;p&gt;If Angular could provide some additional APIs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ViewRef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* This doesn't exist. */&lt;/span&gt;
  &lt;span class="nf"&gt;setCustomSignalChangeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... or something less verbose 😅, we could combine this with &lt;code&gt;ViewRef.detach()&lt;/code&gt; and easily sneak in between Signals and change detection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal-Based Components
&lt;/h3&gt;

&lt;p&gt;As of today, Signal-based components are not implemented yet so there is no way to know if this would work, as implementation details will probably change.&lt;/p&gt;

&lt;h1&gt;
  
  
  ⚛ Custom Render Strategies in some other Libraries &amp;amp; Frameworks
&lt;/h1&gt;

&lt;p&gt;What about other libraries and frameworks?&lt;/p&gt;

&lt;p&gt;I couldn't refrain from asking, so &lt;a href="https://twitter.com/yjaaidi/status/1642193294900842497" rel="noopener noreferrer"&gt;I did&lt;/a&gt; and received interesting feedback from SolidJS's &lt;a href="https://twitter.com/RyanCarniato" rel="noopener noreferrer"&gt;Ryan Carniato&lt;/a&gt; &amp;amp; Preact's &lt;a href="https://twitter.com/_developit" rel="noopener noreferrer"&gt;Jason Miller&lt;/a&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  SolidJS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/RyanCarniato/status/1642956077976399872" rel="noopener noreferrer"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/1u1pnRq07aElFkwoAfjPLa/fd5ab3b48d1d3d1dfdf0879c0ec12b43/solidjs-ryan-carniato-render-strategy-cropped.png" alt="solidjs-ryan-carniato-render-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Preact
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/_developit/status/1643134288555302912" rel="noopener noreferrer"&gt;&lt;img src="//images.ctfassets.net/gowvxq3b4aid/4AEdMShFiCni4ikJVwzdbo/e57db97e52a3d97eb24b58066e24edf2/preact-jason-miller-render-strategy-cropped.png" alt="preact-jason-miller-render-strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  React
&lt;/h3&gt;

&lt;p&gt;In React, no matter if we are using Signals or not, we could implement a &lt;a&gt;Higher Order Component&lt;/a&gt; that decides whether to really render or return a memoized value depending on its strategy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterWithViewportStrategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withViewportStrategy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterWithViewportStrategy&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cf. &lt;a href="https://stackblitz.com/edit/react-custom-render-strategies?file=App.tsx" rel="noopener noreferrer"&gt;React Custom Render Strategies Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This could probably be more efficient with Signals if achieved by wrapping &lt;code&gt;React.createElement&lt;/code&gt; &lt;a href="https://github.com/preactjs/signals/blob/0a585660e141f3d92fb8789c234e69d5a1da8a86/packages/react/src/index.ts#L207" rel="noopener noreferrer"&gt;like Preact Signals integration does&lt;/a&gt; and implementing a custom strategy instead of the &lt;a href="https://github.com/preactjs/signals/blob/0a585660e141f3d92fb8789c234e69d5a1da8a86/packages/react/src/index.ts#L136" rel="noopener noreferrer"&gt;default behavior&lt;/a&gt;.&lt;br&gt;
Or maybe, using a custom hook based on &lt;a href="https://react.dev/reference/react/useSyncExternalStore" rel="noopener noreferrer"&gt;&lt;code&gt;useSyncExternalStore()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Vue.js
&lt;/h3&gt;

&lt;p&gt;Using JSX, we could wrap the &lt;code&gt;render()&lt;/code&gt; just like &lt;code&gt;withMemo()&lt;/code&gt; does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;viewportStrategy&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;rootEl&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rootEl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cf. &lt;a href="https://stackblitz.com/edit/vue-custom-render-strategy?file=src%2Fcomponents%2FCounter.jsx" rel="noopener noreferrer"&gt;throttle example on Stackblitz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... but I'm still wondering how this could work in SFC without having to add a compiler node transform to convert something like &lt;code&gt;v-viewport-strategy&lt;/code&gt; into a wrapper. 🤔&lt;/p&gt;

&lt;h3&gt;
  
  
  Qwik
&lt;/h3&gt;

&lt;p&gt;This one needs a bit more investigation 😅, and I am not sure if overriding the default render strategy is currently feasible. &lt;br&gt;
However, my first guess would be that this can be "qwikly" added to the framework.&lt;br&gt;
For example, there could be an API allowing us to toggle a component's "DETACHED" flag which would skip scheduling component render in &lt;a href="https://github.com/BuilderIO/qwik/blob/9c555682ef8e708c12c8d284aa668853888c981e/packages/qwik/src/core/render/dom/notify-render.ts#L58-L85" rel="noopener noreferrer"&gt;&lt;code&gt;notifyRender()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  👨🏻‍🏫 Closing Observations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ☢️ Please, don't do this at work!
&lt;/h3&gt;

&lt;p&gt;The presented solution is based on internal APIs that might change at any moment, including the next Angular minor or patch versions.&lt;/p&gt;

&lt;p&gt;So why write about it? My goal here is to show some new capabilities that could be enabled thanks to Signals while improving the Developer eXperience at the same time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Wanna try custom render strategies before switching to Signals?
&lt;/h3&gt;

&lt;p&gt;Check out &lt;a href="https://www.rx-angular.io/docs/template" rel="noopener noreferrer"&gt;RxAngular's template&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;While custom render strategies can instantly improve performance in some specific situations, the final note is that you should prefer &lt;strong&gt;keeping a low number of DOM elements&lt;/strong&gt; and &lt;strong&gt;reducing the number of updates&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In other words, keep your apps simple &lt;em&gt;(as much as you can)&lt;/em&gt;, organized, and your data flow  optimized by design using fine-grained reactivity &lt;em&gt;(whether you are using RxJS-based solutions, or Signals)&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔗 Links &amp;amp; Upcoming Workshops
&lt;/h2&gt;

&lt;p&gt;👨🏻‍🏫 &lt;a href="https://marmicode.eventbrite.com" rel="noopener noreferrer"&gt;Workshops&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📰 &lt;a href="https://gmail.us3.list-manage.com/subscribe?u=915d6ba70c9c00912ba326214&amp;amp;id=71255f30c7" rel="noopener noreferrer"&gt;Subscribe to Newsletter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;a href="https://github.com/yjaaidi/experiments/blob/angular-signals-and-custom-render-strategies/src/app/counter.component.ts" rel="noopener noreferrer"&gt;Source Code Repository&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;💬 &lt;a href="https://github.com/marmicode/marmicode/discussions/82" rel="noopener noreferrer"&gt;Discuss this on github&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Wiprecation</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Wed, 09 Dec 2020 22:43:49 +0000</pubDate>
      <link>https://forem.com/marmicode/wiprecation-2hd2</link>
      <guid>https://forem.com/marmicode/wiprecation-2hd2</guid>
      <description>&lt;p&gt;🚧 Work in progress&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiprecation&lt;/strong&gt;: documenting that new code is not ready yet when &lt;a href="https://paulhammant.com/blog/branch_by_abstraction.html" rel="noopener noreferrer"&gt;Branching by Abstraction&lt;/a&gt;, or using &lt;a href="https://medium.com/@kentbeck_7670/test-commit-revert-870bbd756864" rel="noopener noreferrer"&gt;TCR&lt;/a&gt; or Timeboxed TDD.&lt;/p&gt;

</description>
      <category>tdd</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Spice Up your Caching with Convoyr</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Tue, 17 Nov 2020 12:55:33 +0000</pubDate>
      <link>https://forem.com/marmicode/spice-up-your-caching-with-convoyr-4op1</link>
      <guid>https://forem.com/marmicode/spice-up-your-caching-with-convoyr-4op1</guid>
      <description>&lt;h1&gt;
  
  
  Where it All Started
&lt;/h1&gt;

&lt;p&gt;Most web apps get their value from interacting with HTTP APIs.&lt;br&gt;
This is generally done using HTTP clients like the native &lt;code&gt;fetch&lt;/code&gt; function, &lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt; or &lt;a href="https://angular.io/guide/http" rel="noopener noreferrer"&gt;Angular's &lt;code&gt;HttpClient&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you set up an HTTP client on a web app then sooner or later, you will need to extend its capabilities in order to handle different topics like &lt;strong&gt;User Experience&lt;/strong&gt; &lt;em&gt;(e.g. pending requests indicator)&lt;/em&gt;, &lt;strong&gt;performance&lt;/strong&gt; &lt;em&gt;(e.g. caching)&lt;/em&gt;, &lt;strong&gt;resilience&lt;/strong&gt; &lt;em&gt;(e.g. automatic retry)&lt;/em&gt;, and &lt;strong&gt;security&lt;/strong&gt; &lt;em&gt;(e.g. authentication)&lt;/em&gt;. Luckily, most HTTP clients can be easily extended using interceptors so you won't have to wrap them or implement your own client.&lt;/p&gt;

&lt;p&gt;Even though implementing an interceptor can sound quick and easy, handling &lt;strong&gt;edge cases&lt;/strong&gt;, &lt;strong&gt;testing&lt;/strong&gt; and &lt;strong&gt;maintenance&lt;/strong&gt; can get expensive. Wouldn't it be better if someone else could handle these issues for us?&lt;/p&gt;



&lt;p&gt;That's when my friend &lt;a href="https://twitter.com/edbzn" rel="noopener noreferrer"&gt;Edouard Bozon&lt;/a&gt; and I noticed the following facts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;most apps including ours, those of our clients &lt;strong&gt;(i.e. companies not HTTP ones)&lt;/strong&gt; and probably yours need the same interceptors,&lt;/li&gt;
&lt;li&gt;implementing interceptors can be tricky with some HTTP clients if you are not familiar with some other concepts,&lt;/li&gt;
&lt;li&gt;implementations observed in tutorials or in our clients' codebases can be error-prone or miss a couple of important edge cases,&lt;/li&gt;
&lt;li&gt;implementing the same interceptor more than once in a lifetime is a boring waste of time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next thing I remember is that we decided to react by initiating an open-source library called &lt;a href="https://github.com/jscutlery/convoyr" rel="noopener noreferrer"&gt;Convoyr&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  💡 The Idea Behind Convoyr
&lt;/h1&gt;

&lt;p&gt;While Convoyr is currently focused on extending Angular's &lt;code&gt;HttpClient&lt;/code&gt; it has been designed as a modular and framework agnostic set of plugins.&lt;/p&gt;

&lt;p&gt;We like to think of Convoyr as an infrastructure agnostic Service Mesh for web apps and JavaScript... even if we are not there yet.&lt;/p&gt;
&lt;h1&gt;
  
  
  🐢 The Network Latency Performance Problem
&lt;/h1&gt;

&lt;p&gt;Today, in this blog post, we will focus on the performance topic and how to fix network latency issues using Convoyr.&lt;/p&gt;

&lt;p&gt;In most cases, when a user navigates from a route to another on the same web app, the main thing preventing us from displaying the result instantly is the network latency related to fetching data from some remote service.&lt;/p&gt;

&lt;p&gt;This can be problematic, especially when it comes to re-fetching some data that we have just fetched a few minutes ago and that didn't change since. We end up making the user wait to finally display the same result as he received before.&lt;br&gt;
Imagine a list of products where the user clicks on a specific product to see its details before clicking the "back to the list" button. The latency due to re-fetching the products can cause friction.&lt;/p&gt;
&lt;h1&gt;
  
  
  🚒 Caching to the Rescue
&lt;/h1&gt;

&lt;p&gt;One of the first solutions we can think of is caching. We can implement our own caching system or just set the right response headers and let the browser handle the HTTP caching as described by &lt;a href="https://tools.ietf.org/html/rfc7234" rel="noopener noreferrer"&gt;RFC7234&lt;/a&gt;.&lt;br&gt;
The latter approach is generally the most appropriate as it is standard, generic, efficient, scalable, sharable, easy to set up, and cheap to maintain.&lt;/p&gt;
&lt;h1&gt;
  
  
  🍺 The Freshness Problem
&lt;/h1&gt;

&lt;p&gt;HTTP caching is relatively easy to set up but it comes with a price, the price of freshness.&lt;br&gt;
In fact, in order to cache a response, the server has to tell the client how long it can cache it or in other terms, how long it should be considered fresh.&lt;/p&gt;

&lt;p&gt;Choosing a freshness duration can be a challenging decision.&lt;/p&gt;

&lt;p&gt;Too low and it would render the cache useless; too high and the web app would use the "expired" data returned by the cache.&lt;/p&gt;
&lt;h1&gt;
  
  
  🤔 Why make a choice
&lt;/h1&gt;

&lt;p&gt;Software development is all about tradeoffs but what if we could skip this one.&lt;/p&gt;

&lt;p&gt;Wouldn't it be nice if we could use the latest data from the cache while we are fetching the freshest one from the network?&lt;/p&gt;

&lt;p&gt;We can imagine many ways of implementing this behavior but let's focus on Developer eXperience and find an approach that works globally without having to change all the HTTP calls in our apps.&lt;/p&gt;
&lt;h1&gt;
  
  
  Observable vs Promise
&lt;/h1&gt;

&lt;p&gt;Angular's &lt;code&gt;HTTPClient&lt;/code&gt; has the specificity of returning observables instead of promises in opposition to the native &lt;code&gt;fetch&lt;/code&gt; function and Axios.&lt;/p&gt;

&lt;p&gt;Amongst other advantages like making HTTP calls lazy and easily cancelable, observables offer an additional benefit which is the &lt;strong&gt;ability to emit multiple values&lt;/strong&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  ✌️ Emit both cached &amp;amp; network
&lt;/h1&gt;

&lt;p&gt;As we can emit multiple values with observables, what about first emitting the data from the cache &lt;em&gt;(if available)&lt;/em&gt; and then the data from the network?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmvwydmosr00inaya5xjj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmvwydmosr00inaya5xjj.png" alt="Convoyr Cache Plugin Sequence Diagram" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that given the code below in our Angular component, we wouldn't have to change anything and it would first display the cached result and refresh it with the latest result from the network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{{ weather | json }}`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;...()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Weather&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/weather/lyon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or in a more reactive way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{{ weather$ | async | json }}`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;weather$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Weather&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/weather/lyon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Convoyr cache plugin
&lt;/h1&gt;

&lt;p&gt;Convoyr provides a cache plugin &lt;code&gt;@convoyr/plugin-cache&lt;/code&gt; that extending the behavior of the HTTP client by first emitting the data from the cache if available then the one from the network as described above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;It takes two steps to setup Convoyr's cache plugin.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing Convoyr and the plugin:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @convoyr/core @convoyr/angular @convoyr/plugin-cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Enable the cache plugin in the &lt;code&gt;AppModule&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConvoyrModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@convoyr/angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCachePlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@convoyr/plugin-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nx"&gt;HttpClientModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ConvoyrModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;createCachePlugin&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to know if the data comes from cache
&lt;/h2&gt;

&lt;p&gt;You will probably want to display the data differently when it comes from the cache or when it's all fresh from the network.&lt;/p&gt;

&lt;p&gt;Convoyr's cache plugin can provide some metadata on the emitted response by setting the &lt;code&gt;addCacheMetadata&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;createCachePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;addCacheMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful though as this will change the response type.&lt;/p&gt;

&lt;p&gt;The code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/weather/lyon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... will log the following data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;cacheMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2020-01-01T00:00:00.000Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;isFromCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Convoyr's cache plugin is progressive
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;addCacheMetadata&lt;/code&gt; can be very interesting but it is also kind of intrusive as it changes the response type.&lt;br&gt;
Enabling it globally on some apps can require a terrific refactoring.&lt;/p&gt;

&lt;p&gt;In order to avoid the trouble and let you enable this option progressively, the cache plugin allows you to activate different configurations on different groups of requests using the &lt;code&gt;shouldHandleRequest&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;matchOrigin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;matchPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@convoyr/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;createCachePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;shouldHandleRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;matchOrigin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marmicode.io&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;matchPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/weather&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Storage
&lt;/h2&gt;

&lt;p&gt;By default, the cache plugin stores the 100 most recently used requests in memory.&lt;/p&gt;

&lt;p&gt;You can override this behavior by providing your own storage or instantiating the &lt;code&gt;MemoryStorage&lt;/code&gt; with the size of your choice using the &lt;code&gt;maxSize&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;createCachePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MemoryStorage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;maxSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="c1"&gt;// 2000 requests&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;createCachePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MemoryStorage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;maxSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2 mb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="c1"&gt;// 2MB&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🚀 Upcoming Features
&lt;/h1&gt;

&lt;p&gt;This is just the beginning and there is more to come so stay tuned.&lt;/p&gt;

&lt;p&gt;Here is a list of some upcoming features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle ReSTful APIs &lt;em&gt;(e.g. &lt;code&gt;/items&lt;/code&gt; should populate &lt;code&gt;/items/:itemId&lt;/code&gt; so we can instantly show partial data from list views in detail views)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Use IndexedDB as storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  🔌 Other plugins
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/jscutlery/convoyr/tree/master/libs/plugin-auth" rel="noopener noreferrer"&gt;@convoyr/plugin-auth&lt;/a&gt; handles authentication both easily and securely.&lt;br&gt;
&lt;a href="https://github.com/jscutlery/convoyr/tree/master/libs/plugin-retry" rel="noopener noreferrer"&gt;@convoyr/plugin-retry&lt;/a&gt; handles backoff &lt;em&gt;(i.e. retries when things go wrong)&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  📝 Other Articles about Convoyr
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.codamit.dev/introducing-convoyr/" rel="noopener noreferrer"&gt;Introducing Convoyr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codamit.dev/plugin-testing-in-convoyr/" rel="noopener noreferrer"&gt;Plugin Testing in Convoyr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>rxjs</category>
      <category>reactiveprogramming</category>
    </item>
    <item>
      <title>End-to-End HTTP request cancelation with RxJS &amp; NestJS</title>
      <dc:creator>Younes Jaaidi</dc:creator>
      <pubDate>Fri, 21 Feb 2020 09:01:33 +0000</pubDate>
      <link>https://forem.com/marmicode/end-to-end-http-request-cancelation-with-rxjs-nestjs-4gnd</link>
      <guid>https://forem.com/marmicode/end-to-end-http-request-cancelation-with-rxjs-nestjs-4gnd</guid>
      <description>&lt;p&gt;Life is too short. When searching for something, we can’t afford to type a whole word or sentence in a search field, or filling all the fields then hitting our old keyboard’s half-broken enter key to finally be able to see the first results... or nothing at all because our search criteria were too restrictive.&lt;/p&gt;

&lt;p&gt;Don’t look at me like that! We can probably agree that most of us, if not all, are &lt;strong&gt;used to features like typeahead and live search results&lt;/strong&gt;. We get frustrated every time we have to submit a search form.&lt;/p&gt;

&lt;h1&gt;
  
  
  TL;DR:
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;if you are using &lt;a href="https://nestjs.com/" rel="noopener noreferrer"&gt;NestJS&lt;/a&gt;, &lt;strong&gt;you will need this interceptor&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;if you are not using &lt;a href="https://nestjs.com/" rel="noopener noreferrer"&gt;NestJS&lt;/a&gt; then &lt;strong&gt;maybe you should&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;we have to &lt;strong&gt;think reactively&lt;/strong&gt;, I agree that it can have a steep learning curve but think about the pleasure of sliding on the other side of the hill ⛷,&lt;/li&gt;
&lt;li&gt;we can and should use &lt;strong&gt;&lt;a href="https://rxjs-dev.firebaseapp.com/" rel="noopener noreferrer"&gt;RxJS&lt;/a&gt; everywhere&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;we should &lt;strong&gt;use observables even for single value streams&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;we should &lt;strong&gt;not ignore observables teardown logic&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  🚨 Reactive Programming &amp;amp; RxJS to the rescue
&lt;/h1&gt;

&lt;p&gt;Implementing these kinds of features can be tricky, especially if developed from scratch and with an imperative approach. That’s when reactive programming and RxJS come to the rescue. In fact, RxJS provides the right tooling and operators to implement these features in a few lines.&lt;br&gt;
RxJS is such a perfect fit for these scenarios that most courses and tutorials cover the live search topic. It helps understand both how reactive programming works and how it can easily solve some challenging issues.&lt;/p&gt;

&lt;p&gt;That’s when we end up with this common recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;keywords$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keywordsControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueChanges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keywords$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="cm"&gt;/* Wait for the user to stop typing for 100ms and emit last value. */&lt;/span&gt;
  &lt;span class="nf"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="cm"&gt;/* Ignore identical successive values
   * (e.g. user pastes the same value in the input). */&lt;/span&gt;
  &lt;span class="nf"&gt;distinctUntilChanged&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
  &lt;span class="cm"&gt;/* when new keywords are emitted, this unsubscribes from the previous
   * search result (canceling the underlying http request)
   * and subscribes to the new one. */&lt;/span&gt;
  &lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The illustration below might help you notice the difference between &lt;a href="https://slides.com/yjaaidi/rxjs-flattening-strategy" rel="noopener noreferrer"&gt;RxJS flattening strategies&lt;/a&gt; and the related operators:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiiw632jrlizoo33rhm4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiiw632jrlizoo33rhm4d.png" alt="RxJS Flattening Strategies" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;but if it doesn't help, you should definitely check out the great work by my buddy Shai Reznik: &lt;a href="https://medium.com/@shairez/a-super-ninja-trick-to-learn-rxjss-switchmap-mergemap-concatmap-and-exhaustmap-forever-88e178a75f1b" rel="noopener noreferrer"&gt;https://medium.com/@shairez/a-super-ninja-trick-to-learn-rxjss-switchmap-mergemap-concatmap-and-exhaustmap-forever-88e178a75f1b&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  🐢 Hey Debounce! Stop bullying my low latency!
&lt;/h1&gt;

&lt;p&gt;The problem is that you are probably investing a lot of energy and money in producing low latency architectures and APIs but all these &lt;strong&gt;efforts just vanish when we introduce the artificial latency created by the &lt;a href="https://rxjs-dev.firebaseapp.com/api/operators/debounceTime" rel="noopener noreferrer"&gt;&lt;code&gt;debounceTime&lt;/code&gt;&lt;/a&gt; operator.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What if we just get rid of the debounce? We are using &lt;a href="https://rxjs-dev.firebaseapp.com/api/operators/switchMap" rel="noopener noreferrer"&gt;&lt;code&gt;switchMap&lt;/code&gt;&lt;/a&gt; after all, and unnecessary requests are immediately canceled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzafrrtqydgh10qkir5ra.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzafrrtqydgh10qkir5ra.gif" alt="Request cancelation" width="598" height="856"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait a second! What happens on the back-end? Is the back-end "work" interrupted by some voodoo magic? Or &lt;strong&gt;did we just trigger some crazy chaos&lt;/strong&gt; where the back-end is working for nothing until it realizes that the consumer is not there anymore?&lt;/p&gt;

&lt;h1&gt;
  
  
  🐈 Here comes the big cat
&lt;/h1&gt;

&lt;p&gt;In a few words, NestJS is &lt;strong&gt;THE feature-rich NodeJS framework&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Amongst its wealth of features, there is &lt;strong&gt;native support of observables&lt;/strong&gt;. This is quite handy even if we respond with a single value and not a stream of values. In fact, &lt;strong&gt;the interesting observables property we are looking for here is cancelability&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧨 Observables Teardown Logic
&lt;/h1&gt;

&lt;p&gt;Observables are said &lt;strong&gt;cancelable&lt;/strong&gt; because we can unsubscribe whenever we need to, and interrupt the work. Cancelation works thanks to the teardown logic &lt;strong&gt;function returned when creating an observable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an example of wrapping &lt;code&gt;setInterval&lt;/code&gt; in an observable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="cm"&gt;/* This is the teardown logic. */&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the observer function given to the &lt;code&gt;Observable&lt;/code&gt;'s constructor returns the &lt;strong&gt;teardown logic function that calls &lt;code&gt;clearInterval&lt;/code&gt;&lt;/strong&gt; in order to cancel the tasks scheduled by &lt;code&gt;setInterval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;This is exactly how you should NOT implement an interval.&lt;/strong&gt;&lt;br&gt;
This implementation is &lt;a href="https://rxjs-dev.firebaseapp.com/guide/scheduler" rel="noopener noreferrer"&gt;scheduler&lt;/a&gt; naive.&lt;br&gt;
You should use &lt;a href="https://rxjs-dev.firebaseapp.com/api/index/function/interval" rel="noopener noreferrer"&gt;&lt;code&gt;interval&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://rxjs-dev.firebaseapp.com/api/index/function/timer" rel="noopener noreferrer"&gt;&lt;code&gt;timer&lt;/code&gt;&lt;/a&gt; instead.&lt;/p&gt;
&lt;h1&gt;
  
  
  🧪 The experiment
&lt;/h1&gt;

&lt;p&gt;For the experiment, I needed to run some slow CPU, filesystem and memory intensive work on the back-end for every request. The first idea that crossed my mind was reading a big text file line by line and matching every one of them against the given keywords.&lt;br&gt;
It turned out that even with a 1GB file, it was still quite fast.&lt;/p&gt;

&lt;p&gt;That’s when I thought that &lt;strong&gt;reading multiple small files should be more inefficient&lt;/strong&gt;. I just needed to generate a directory with lots of files... but wait! &lt;strong&gt;What about using &lt;code&gt;node_modules&lt;/code&gt; directory&lt;/strong&gt; 🤔&lt;/p&gt;

&lt;p&gt;Bingo! It could not be worse and that is exactly what I needed.&lt;/p&gt;

&lt;p&gt;The implementation looks something like this and as you can see, the &lt;strong&gt;teardown logic immediately stops crawling&lt;/strong&gt; the directory and reading files &lt;strong&gt;when the observer unsubscribes&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;directoryPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;walker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;readLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Line&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nodeModulesPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;mergeMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;readLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  😔 The disappointment
&lt;/h1&gt;

&lt;p&gt;In the animation below, we can observe high CPU usage and an exponential memory usage on the back-end and that &lt;strong&gt;canceling the requests, even the last one, doesn’t interrupt the work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F55gms8nbi5puw5atuscn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F55gms8nbi5puw5atuscn.gif" alt="CPU &amp;amp; Memory Usage" width="560" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By diving a little bit in &lt;a href="https://github.com/nestjs/nest/blob/8755571094524f28e2792472cac4cc0171b29e1b/packages/core/router/router-response-controller.ts#L49:L54" rel="noopener noreferrer"&gt;Nest’s source code&lt;/a&gt;, we can see that our observable is converted to a promise using &lt;code&gt;toPromise&lt;/code&gt; method. In fact, Nest has to adapt to frameworks like ExpressJS that don’t handle observables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;transformToResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resultOrDeferred&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resultOrDeferred&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resultOrDeferred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resultOrDeferred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resultOrDeferred&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🔍 Detecting request cancelation
&lt;/h1&gt;

&lt;p&gt;In Nest, request objects are instances of NodeJS’ &lt;a href="https://nodejs.org/api/http.html#http_class_http_incomingmessage" rel="noopener noreferrer"&gt;&lt;code&gt;IncomingMessage&lt;/code&gt;&lt;/a&gt; that &lt;strong&gt;trigger a &lt;code&gt;close&lt;/code&gt; event when the connection is closed or when the HTTP2 stream is closed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If we can detect when the request is canceled, then we can interrupt the work in our RxJS response stream.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aborted&lt;/code&gt; vs &lt;code&gt;close&lt;/code&gt;:
&lt;code&gt;IncomingMessage&lt;/code&gt; also triggers an &lt;code&gt;aborted&lt;/code&gt; event that you can ignore as it will probably be deprecated in the future.
Cf. &lt;a href="https://github.com/nodejs/node/issues/15456" rel="noopener noreferrer"&gt;https://github.com/nodejs/node/issues/15456&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/nodejs/node/issues/15525" rel="noopener noreferrer"&gt;https://github.com/nodejs/node/issues/15525&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nest has an interesting concept called &lt;strong&gt;interceptors&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Interceptors have a set of useful capabilities which are inspired by the &lt;a href="https://en.wikipedia.org/wiki/Aspect-oriented_programming" rel="noopener noreferrer"&gt;Aspect Oriented Programming (AOP)&lt;/a&gt; technique.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NoopInterceptor&lt;/span&gt; &lt;span class="kr"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;NestInterceptor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExecutionContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CallHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it possible to write, &lt;strong&gt;in a single place&lt;/strong&gt;, a function that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;intercepts&lt;/strong&gt; every incoming HTTP request,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;listens&lt;/strong&gt; to the request’s &lt;code&gt;close&lt;/code&gt; event,&lt;/li&gt;
&lt;li&gt;does something to &lt;strong&gt;interrupt&lt;/strong&gt; the work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the interesting properties of Nest interceptors, compared to Express middlewares for example, is that the &lt;code&gt;next&lt;/code&gt; parameter is not just a function that triggers the route function or the next middleware but &lt;strong&gt;it is an object with a &lt;code&gt;handle&lt;/code&gt; method that returns an &lt;code&gt;Observable&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to this feature, &lt;strong&gt;we can manipulate the response and the whole stream by adding operators to the given &lt;code&gt;Observable&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For instance, we can detect the request cancelation by listening to the &lt;code&gt;close&lt;/code&gt; event using RxJS's &lt;a href="https://rxjs-dev.firebaseapp.com/api/index/function/fromEvent" rel="noopener noreferrer"&gt;&lt;code&gt;fromEvent&lt;/code&gt;&lt;/a&gt; and interrupt the &lt;code&gt;Observable&lt;/code&gt; returned by the route handler using the &lt;a href="https://rxjs-dev.firebaseapp.com/api/operators/takeUntil" rel="noopener noreferrer"&gt;&lt;code&gt;takeUntil&lt;/code&gt;&lt;/a&gt; operator.&lt;/p&gt;

&lt;p&gt;The final interceptor should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnsubscribeOnCloseInterceptor&lt;/span&gt; &lt;span class="kr"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;NestInterceptor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExecutionContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CallHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switchToHttp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;close$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fromEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;takeUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;close$&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try it out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc60e6qkvdxuxekmht1ed.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc60e6qkvdxuxekmht1ed.gif" alt="CPU &amp;amp; memory usage with interceptor" width="600" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can observe, thanks to the interceptor, &lt;strong&gt;canceling an HTTP request will automatically and almost immediately cancel the work by unsubscribing from the observable returned by the route handler&lt;/strong&gt;.&lt;br&gt;
This reduces CPU, memory and all resources usage and interrupts all the work even when the user simply closes the window.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧠 Think reactive
&lt;/h1&gt;

&lt;p&gt;The key takeaway here is that &lt;strong&gt;by adopting a reactive approach and using observables everywhere, we can easily benefit from observables cancelability and boost APIs performance&lt;/strong&gt; with a generic interceptor.&lt;/p&gt;

&lt;h1&gt;
  
  
  MongoDB query cancelation
&lt;/h1&gt;

&lt;p&gt;What if our data source was a database like MongoDB? Can we interrupt the query?&lt;br&gt;
📻 Stay tuned for an upcoming post on this topic 😉&lt;/p&gt;

&lt;h1&gt;
  
  
  👨🏻‍🍳 Let me help you!
&lt;/h1&gt;

&lt;p&gt;At &lt;a href="https://marmicode.io" rel="noopener noreferrer"&gt;Marmicode&lt;/a&gt;, we use our passion and experience in Web Development &amp;amp; eXtreme Programming to help you cook better apps, ship them fast and make you proud of your work.&lt;/p&gt;

&lt;p&gt;We have the services you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code Review,&lt;/li&gt;
&lt;li&gt;Remote Consultations,&lt;/li&gt;
&lt;li&gt;Workshops,&lt;/li&gt;
&lt;li&gt;On-demand development &lt;em&gt;billed by accepted points&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📨 kitchen at marmicode.io&lt;/p&gt;

&lt;h1&gt;
  
  
  🔗 Links
&lt;/h1&gt;

&lt;p&gt;💻 &lt;a href="https://github.com/yjaaidi/ng-experiments/tree/http-request-cancelation" rel="noopener noreferrer"&gt;Source code&lt;/a&gt; Nx monorepo with an Angular app, a NestJS API and custom CPU / Memory graphing app using Angular &amp;amp; GraphQL subscriptions.&lt;br&gt;
🐦 &lt;a href="https://twitter.com/intent/follow?screen_name=yjaaidi" rel="noopener noreferrer"&gt;@yjaaidi&lt;/a&gt; Stay tuned for more posts and upcoming workshops.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>rxjs</category>
      <category>node</category>
    </item>
  </channel>
</rss>
