<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Zak Henry</title>
    <subtitle>software engineer, tinkerer</subtitle>
    <link rel="self" type="application/atom+xml" href="https://zakhenry.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://zakhenry.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-01-10T00:00:00+00:00</updated>
    <id>https://zakhenry.com/atom.xml</id>
    <entry xml:lang="en">
        <title>Running gRPC services on AWS Lambda with Rust</title>
        <published>2026-01-10T00:00:00+00:00</published>
        <updated>2026-01-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Zak Henry
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zakhenry.com/serverless-grpc-with-lambda-grpc-web/"/>
        <id>https://zakhenry.com/serverless-grpc-with-lambda-grpc-web/</id>
        
        <content type="html" xml:base="https://zakhenry.com/serverless-grpc-with-lambda-grpc-web/"> &lt;!----&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-[var(--admonition-bg)]&quot;
  style=&quot;--admonition-bg: rgba(0, 191, 165, 0.1)&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-[var(--admonition-bg)] p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(.&#x2F;icons&#x2F;tip.svg)&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;tldr&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;Rust gRPC service running on AWS Lambda with grpc-web message transport. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;lambda-grpc-web&quot;&gt;Yes please&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;setting-the-scene&quot;&gt;Setting the scene&lt;&#x2F;h2&gt;
&lt;p&gt;It is January 2026, we are well past the honeymoon phase of serverless compute, and by and large discourse among
developers is dominated by AI talk. Serverless compute has many well-justified arguments &lt;em&gt;against&lt;&#x2F;em&gt; its use, from high
cost, to poor performance, and difficult debugging and observability. It also has many excellent qualities, such as
scale-to-zero costing, fully managed vulnerability patching etc.&lt;&#x2F;p&gt;
&lt;p&gt;Some teams may swear by the platform, where others swear off it entirely.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve largely been in the latter camp. I have deployed some Node.js Lambda functions for integrating with AWS Cognito in
the past, and have some experience with Python lambdas. These experiences were ok, but for various reasons the projects
suffered from code rot, bugs were very difficult to trace, and the local development story was sorely lacking.
&lt;strong&gt;It has never been a tool that I&#x27;ve reached for when a docker container running in ECS would do just fine&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Combine this with the last 8 years of API development using strictly &lt;a href=&quot;https:&#x2F;&#x2F;grpc.io&#x2F;&quot;&gt;gRPC&lt;&#x2F;a&gt; services, and writing more
and more Rust lately, I&#x27;ve found myself quite separate from the world of REST and untyped languages Lambdas seem to be
tailored for.&lt;&#x2F;p&gt;
&lt;p&gt;That said, I have been &lt;strong&gt;wanting&lt;&#x2F;strong&gt; to use Lambdas for a long time - they seem like a great solution to the niche where
you have low traffic - why have a server sitting there doing &lt;em&gt;nothing&lt;&#x2F;em&gt; 99% of the time? Even if it is bin packed into an
EC2 instance, scale up events of many of these will cause latency spikes etc. For something like an admin dashboard
where a handful of people might make API calls a few times a day, serverless deployment is likely a perfect fit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;defining-requirements&quot;&gt;Defining requirements&lt;&#x2F;h2&gt;
&lt;p&gt;There are three key requirements I would need to resolve before floating the idea of introducing Lambda into our
deployment options&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Performance must not be meaningfully impacted (concretely, users must not notice cold starts)&lt;&#x2F;li&gt;
&lt;li&gt;Protocol must be gRPC compatible for consistency with other deployed services&lt;&#x2F;li&gt;
&lt;li&gt;It must be easy to switch back to ECS if usage reaches the threshold where Lambda is not cheaper&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For the performance issue, I eliminate the interpreted and virtual machine options as they
&lt;a href=&quot;https:&#x2F;&#x2F;maxday.github.io&#x2F;lambda-perf&#x2F;&quot;&gt;all have poor cold  start performance&lt;&#x2F;a&gt;.
My preferred language to develop in across the stack is Rust, and it has excellent performance on
cold starts (typically &amp;lt;20ms). AWS &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;about-aws&#x2F;whats-new&#x2F;2025&#x2F;11&#x2F;aws-lambda-rust&#x2F;&quot;&gt;in late 2025 announced general availability of their rust runtime&lt;&#x2F;a&gt;,
so it seems to be a good fit for my goals.&lt;&#x2F;p&gt;
&lt;p&gt;gRPC is the sticking point now, as it isn&#x27;t going to just work out of the box with a Rust gRPC service. Lambda doesn&#x27;t
support &lt;code&gt;http&#x2F;2&lt;&#x2F;code&gt;, which gRPC requires. The Rust de-facto gRPC framework &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hyperium&#x2F;tonic&quot;&gt;tonic&lt;&#x2F;a&gt; is
explicitly built around &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hyperium&#x2F;hyper&quot;&gt;hyper&lt;&#x2F;a&gt;, which is a tcp socket binding service, but Lambda
functions don&#x27;t support that.&lt;&#x2F;p&gt;
&lt;p&gt;This showed to me there was a need for something to cleanly glue these elements together, in a way that was easily
reversed should developers want to go back to more standard deployment models.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;introducing-lambda-grpc-web&quot;&gt;Introducing &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;lambda-grpc-web&quot;&gt;&lt;code&gt;lambda-grpc-web&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This is a fairly small crate, with a few design goals in mind&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It must be easy to switch back and forth between deployment models&lt;&#x2F;li&gt;
&lt;li&gt;Limitations of Lambda environment are minimised (all features of tonic that lambda can support, must be supported)&lt;&#x2F;li&gt;
&lt;li&gt;It is &lt;em&gt;not&lt;&#x2F;em&gt; batteries included. Dev still maintains control of local development, testing and deployment flows&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Let&#x27;s see how this looks, with a simple hello world demo&lt;&#x2F;p&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo&lt;&#x2F;h2&gt;
&lt;p&gt;We are going to take the tonic hello world service, enable grpc-web, add a unit test and convert it to a lambda service,
and deploy it to AWS Lambda.&lt;&#x2F;p&gt;
 &lt;!----&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-[var(--admonition-bg)]&quot;
  style=&quot;--admonition-bg: rgba(101, 31, 255, 0.1)&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-[var(--admonition-bg)] p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(.&#x2F;icons&#x2F;example.svg)&quot;
    &gt;
      example
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;source&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;For the runnable demo, refer to the Rust project at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;zakhenry.com&#x2F;tree&#x2F;master&#x2F;demos&#x2F;hello-lambda-grpc-web&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;zakhenry.com&#x2F;tree&#x2F;master&#x2F;demos&#x2F;hello-lambda-grpc-web&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;writing-a-standard-grpc-tcp-service&quot;&gt;Writing a standard gRPC TCP service&lt;&#x2F;h3&gt;
&lt;p&gt;This project is a very basic hello world grpc service with a single unary endpoint:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;proto&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-proto &quot;&gt;&lt;code class=&quot;language-proto&quot; data-lang=&quot;proto&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; demo&#x2F;proto&#x2F;hello_world.proto#L4-L6
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;service &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Greeter &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;rpc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;SayHello &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;HelloRequest&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;returns &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;HelloReply&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The implementation is barebones hello world&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; demo&#x2F;src&#x2F;bin&#x2F;tcp-server.rs#L6-L29
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span&gt;hello_world {
&lt;&#x2F;span&gt;&lt;span&gt;    tonic::include_proto!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;helloworld&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Debug, Default)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span&gt;MyGreeter {}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tonic&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;async_trait&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Greeter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;MyGreeter {
&lt;&#x2F;span&gt;&lt;span&gt;    async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;say_hello&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;: Request&amp;lt;HelloRequest&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ) -&amp;gt; Result&amp;lt;Response&amp;lt;HelloReply&amp;gt;, Status&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Got a request: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{:?}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, request);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; reply = HelloReply {
&lt;&#x2F;span&gt;&lt;span&gt;            message: format!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, request.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into_inner&lt;&#x2F;span&gt;&lt;span&gt;().name),
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        Ok(Response::new(reply))
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The main function configures the tonic server builder as follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; demo&#x2F;src&#x2F;bin&#x2F;tcp-server.rs#L30-L42
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokio&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; greeter = MyGreeter::default();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Server::builder()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;accept_http1&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;layer&lt;&#x2F;span&gt;&lt;span&gt;(GrpcWebLayer::new())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;add_service&lt;&#x2F;span&gt;&lt;span&gt;(GreeterServer::new(greeter))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serve&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;127.0.0.1:9000&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .await?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
 &lt;!----&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-[var(--admonition-bg)]&quot;
  style=&quot;--admonition-bg: rgba(100, 221, 23, 0.1)&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-[var(--admonition-bg)] p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(.&#x2F;icons&#x2F;question.svg)&quot;
    &gt;
      question
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;grpc-web?&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;You may notice we have a &lt;code&gt;GrpcWebLayer&lt;&#x2F;code&gt; configured here. We&#x27;ll discuss it in more detail later but for now it is
important to understand that it is a hard requirement to have an http&#x2F;1 compatible transport in order to have gRPC
work with the AWS Lambda infrastructure.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Ok now we can run this service locally with&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;shell&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-shell &quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;cargo run
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And we will see our service start happily. However now we need a client to connect to the service.&lt;&#x2F;p&gt;
&lt;p&gt;Simplest option right now is to create an integration test, since we&#x27;re going to be wanting that for our own testing
stack anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Other than a somewhat verbose client setup, the test is straightforward. Make a request, assert on the response.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; demo&#x2F;tests&#x2F;tcp-server.rs#L43-L54
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokio&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;unary_test&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; request = tonic::Request::new(HelloRequest {
&lt;&#x2F;span&gt;&lt;span&gt;        name: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;grpc web client&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; response = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;make_greeter_client&lt;&#x2F;span&gt;&lt;span&gt;()?.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;say_hello&lt;&#x2F;span&gt;&lt;span&gt;(request).await?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    assert_eq!(response.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into_inner&lt;&#x2F;span&gt;&lt;span&gt;(), HelloReply { message: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello grpc web client!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;() });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;converting-our-service-to-run-on-aws-lambda&quot;&gt;Converting our service to run on AWS Lambda&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have a regular working gRPC service working locally, lets convert it to a local Lambda.&lt;&#x2F;p&gt;
&lt;p&gt;If you haven&#x27;t already, you&#x27;ll need to set up the excellent tool &lt;a href=&quot;https:&#x2F;&#x2F;www.cargo-lambda.info&#x2F;&quot;&gt;&lt;code&gt;cargo lambda&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; which
handles emulation of the lambda runtime.&lt;&#x2F;p&gt;
&lt;p&gt;Now for the code changes.&lt;&#x2F;p&gt;
&lt;p&gt;First install &lt;code&gt;lambda-grpc-web&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;shell&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-shell &quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;cargo add lambda-grpc-web
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;swap out &lt;code&gt;tonic::transport::server::Server&lt;&#x2F;code&gt; for &lt;code&gt;tonic::transport::Server&lt;&#x2F;code&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;remove the grpc web layer (this is part of lambda-grpc-web)&lt;&#x2F;li&gt;
&lt;li&gt;remove the &lt;code&gt;accept_http1&lt;&#x2F;code&gt; rule (implicit for lambdas; there is no alternative)&lt;&#x2F;li&gt;
&lt;li&gt;remove the socket address from &lt;code&gt;.serve&lt;&#x2F;code&gt; (socket is handled by internal lambda machinery)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;And that&#x27;s it!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;figcaption&gt;Before&lt;&#x2F;figcaption&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; demo&#x2F;src&#x2F;bin&#x2F;tcp-server.rs#L30-L42
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokio&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; greeter = MyGreeter::default();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Server::builder()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;accept_http1&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;layer&lt;&#x2F;span&gt;&lt;span&gt;(GrpcWebLayer::new())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;add_service&lt;&#x2F;span&gt;&lt;span&gt;(GreeterServer::new(greeter))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serve&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;127.0.0.1:9000&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .await?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;figure&gt;
&lt;figure&gt;
    &lt;figcaption&gt;After&lt;&#x2F;figcaption&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; demo&#x2F;src&#x2F;bin&#x2F;lambda-server.rs#L30-L40
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokio&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Result&amp;lt;(), Error&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; greeter = MyGreeter::default();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    LambdaServer::builder()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;add_service&lt;&#x2F;span&gt;&lt;span&gt;(GreeterServer::new(greeter))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;serve&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .await?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;As you can see, the difference between a TCP service and one that runs on AWS Lambda is intentionally kept minimal.&lt;&#x2F;p&gt;
&lt;details&gt;
  &lt;summary&gt;Expand for full diff&lt;&#x2F;summary&gt;
&lt;figure&gt;
    &lt;figcaption&gt;Server implementation&lt;&#x2F;figcaption&gt;
&lt;!-- embedme impl.patch --&gt;
&lt;pre data-lang=&quot;patch&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-patch &quot;&gt;&lt;code class=&quot;language-patch&quot; data-lang=&quot;patch&quot;&gt;&lt;span&gt;diff --git a&#x2F;demo&#x2F;src&#x2F;bin&#x2F;tcp-server.rs b&#x2F;demo&#x2F;src&#x2F;bin&#x2F;lambda-server.rs
&lt;&#x2F;span&gt;&lt;span&gt;index 4fc3d58..80cbf77 100644
&lt;&#x2F;span&gt;&lt;span&gt;--- a&#x2F;demo&#x2F;src&#x2F;bin&#x2F;tcp-server.rs
&lt;&#x2F;span&gt;&lt;span&gt;+++ b&#x2F;demo&#x2F;src&#x2F;bin&#x2F;lambda-server.rs
&lt;&#x2F;span&gt;&lt;span&gt;@@ -1,8 +1,8 @@
&lt;&#x2F;span&gt;&lt;span&gt; use hello_world::greeter_server::{Greeter, GreeterServer};
&lt;&#x2F;span&gt;&lt;span&gt; use hello_world::{HelloReply, HelloRequest};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-use tonic::transport::Server;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;+use lambda_grpc_web::LambdaServer;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;+use lambda_grpc_web::lambda_runtime::Error;
&lt;&#x2F;span&gt;&lt;span&gt; use tonic::{Request, Response, Status};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-use tonic_web::GrpcWebLayer;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt; mod hello_world {
&lt;&#x2F;span&gt;&lt;span&gt;     tonic::include_proto!(&amp;quot;helloworld&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;@@ -28,14 +28,12 @@ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;impl Greeter for MyGreeter {
&lt;&#x2F;span&gt;&lt;span&gt; }
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt; #[tokio::main]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-async fn main() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;+async fn main() -&amp;gt; Result&amp;lt;(), Error&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;     let greeter = MyGreeter::default();
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-    Server::builder()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-        .accept_http1(true)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-        .layer(GrpcWebLayer::new())
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;+    LambdaServer::builder()
&lt;&#x2F;span&gt;&lt;span&gt;         .add_service(GreeterServer::new(greeter))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-        .serve(&amp;quot;127.0.0.1:9000&amp;quot;.parse().unwrap())
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;+        .serve()
&lt;&#x2F;span&gt;&lt;span&gt;         .await?;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt;     Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;It should be trivial to switch to Lambda deploy, and back if the workload proves unsuitable. You might even consider
having two binaries with just the different main boostrap function so you can compare both approaches, or even
dynamically switch between strategies for some specific scenario like pre-scheduled high load.&lt;&#x2F;p&gt;
&lt;p&gt;With our gRPC service converted to lambda, we can now run it locally and run the integration tests against it&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;shell&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-shell &quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;cargo lambda watch
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and run the integration tests against it&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;shell&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-shell &quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;cargo test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
 &lt;!----&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-[var(--admonition-bg)]&quot;
  style=&quot;--admonition-bg: rgba(0, 191, 165, 0.1)&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-[var(--admonition-bg)] p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(.&#x2F;icons&#x2F;tip.svg)&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;Tip&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;If you have more than one binary in your project, &lt;code&gt;cargo lambda&lt;&#x2F;code&gt; is unable to determine which function url you are
attempting to reach, so you will need to change your test uri to &lt;code&gt;http:&#x2F;&#x2F;127.0.0.1:9000&#x2F;lambda-url&#x2F;{binary-name}&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;deploying-to-aws&quot;&gt;Deploying to AWS&lt;&#x2F;h3&gt;
&lt;p&gt;To deploy to AWS refer to the &lt;a href=&quot;https:&#x2F;&#x2F;www.cargo-lambda.info&#x2F;commands&#x2F;deploy.html&quot;&gt;cargo lambda documentation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
 &lt;!----&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-[var(--admonition-bg)]&quot;
  style=&quot;--admonition-bg: rgba(255, 145, 0, 0.1)&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-[var(--admonition-bg)] p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(.&#x2F;icons&#x2F;warning.svg)&quot;
    &gt;
      warning
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;Warning&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;To quick test you&#x27;ll need to make the function url fully public, but be wary of the potential expense this might incur
if the function url is published.&lt;&#x2F;p&gt;
&lt;p&gt;In a real production environment you will likely want to protect the lambda with auth, or have Cloudflare WAF or similar
if the endpoint is supposed to be unauthenticated.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Once deployed, update the uri to the generated function url and rerun your integration test. &lt;strong&gt;gRPC in AWS Lambda!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;further-features&quot;&gt;Further features&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;lambda-grpc-web&lt;&#x2F;code&gt; also supports the other features tonic provides, so it should be a breeze to enable existing &lt;code&gt;tower&lt;&#x2F;code&gt;
layers, or service-specific &lt;code&gt;tonic&lt;&#x2F;code&gt; interceptors etc.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, to avoid confusing errors there is a specific panic handler defined which converts any panic into a gRPC
&lt;code&gt;13 INTERNAL&lt;&#x2F;code&gt; error, which plays nicely with existing gRPC clients. This is gated as a crate feature that can be turned
off if you want to define your own handler.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;tradeoffs&quot;&gt;Tradeoffs&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately due to limitations of the lambda runtime, there are tradeoffs to be aware of, and consider up front if
they are disqualifying for your intended use-case.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;grpc-web&quot;&gt;grpc-web&lt;&#x2F;h3&gt;
&lt;p&gt;Mentioned earlier, we are required to downgrade the message transport with the grpc-web protocol as AWS Lambda does not
support http&#x2F;2.&lt;&#x2F;p&gt;
 &lt;!----&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-[var(--admonition-bg)]&quot;
  style=&quot;--admonition-bg: rgba(100, 221, 23, 0.1)&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-[var(--admonition-bg)] p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(.&#x2F;icons&#x2F;question.svg)&quot;
    &gt;
      question
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;What is grpc-web?&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;grpc-web is effectively a constrained gRPC dialect designed to survive hostile HTTP&#x2F;1 infrastructure.
It removes some of the stream capabilities, flow control, and packs trailers into the body.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Depending on target client, this isn&#x27;t a huge tradeoff, especially as you might already be wanting to connect to the
service with a browser, and so would already need to have either a conversion at the service, or a converting reverse
proxy like &lt;a href=&quot;https:&#x2F;&#x2F;www.envoyproxy.io&quot;&gt;Envoy&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A common frustration with grpc-web is that common testing tools like Postman don&#x27;t natively work with grpc-web.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;client-cancellation&quot;&gt;Client cancellation&lt;&#x2F;h3&gt;
&lt;p&gt;Since Lambda doesn&#x27;t manage the TCP connection, it is unaware of the client disconnecting, and does not signal to the
runtime that there is nothing on the other end to respond to. This can result in certain workloads spending more on
compute than necessary.
When designing an endpoint, it is important to make server side response deadlines and implement cancellation if the
duration is expected to be non-trivial (and is cancellable).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;time-limit&quot;&gt;Time limit&lt;&#x2F;h3&gt;
&lt;p&gt;AWS Lambda has a hard time limit of 15 minutes, which makes this approach unsuited for long term streaming requests.
However, due to lambda costing more for long durations anyway this is not a good fit for that kind of workload anyway.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cost&quot;&gt;Cost&lt;&#x2F;h3&gt;
&lt;p&gt;The niche this approach fills is explicitly low traffic volumes. With sustained traffic, ECS is cheaper and better.
&lt;sup&gt;&lt;em&gt;This might change with Lambda Managed&lt;&#x2F;em&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
 &lt;!----&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-[var(--admonition-bg)]&quot;
  style=&quot;--admonition-bg: rgba(0, 176, 255, 0.1)&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-[var(--admonition-bg)] p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(.&#x2F;icons&#x2F;abstract.svg)&quot;
    &gt;
      abstract
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;This is &lt;em&gt;not&lt;&#x2F;em&gt; intended as a general replacement for gRPC servers or REST APIs — it&#x27;s a deliberately narrow solution for
low-traffic, gRPC-first stacks.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;future-work&quot;&gt;Future work&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;lambda-managed&quot;&gt;Lambda Managed&lt;&#x2F;h3&gt;
&lt;p&gt;In November, AWS announced &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;lambda&#x2F;lambda-managed-instances&quot;&gt;Lambda Managed&lt;&#x2F;a&gt; which allows lambda
functions to be run on your EC2 instances, effectively eliminating the main cost downsides of Lambda. It also adds
concurrency support. The rust runtime has experimental support for this, and I intend to add explicit support for it in
&lt;code&gt;lambda-grpc-web&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;envoy-reverse-conversion&quot;&gt;Envoy reverse conversion&lt;&#x2F;h3&gt;
&lt;p&gt;Since there is a good chance stacks might have Envoy as service mesh management, I&#x27;ve been tinkering with developing a
reverse conversion such that clients with envoy in between could make &lt;em&gt;either&lt;&#x2F;em&gt; gRPC standard, or grpc-web calls and it
would just work. This is a work in progress as I &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;envoyproxy&#x2F;envoy&#x2F;issues&#x2F;42559&quot;&gt;ran into a roadblock&lt;&#x2F;a&gt;
with Envoy custom extensions lacking some required features.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap up&lt;&#x2F;h2&gt;
&lt;p&gt;I hope this new capability finds itself some interesting niches for use in the world. I&#x27;m intending to use it as a dirt
cheap (likely well inside free tier) way of deploying low traffic APIs.&lt;&#x2F;p&gt;
&lt;p&gt;Please have a look at the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;lambda-grpc-web&quot;&gt;repo&lt;&#x2F;a&gt;, there are examples and integration tests
that explore more capability like streaming and middleware.&lt;&#x2F;p&gt;
&lt;p&gt;As a final note, I did this not because I think it should be the next big thing, I did it because it&#x27;s an interesting
challenge, and in a relatively unexplored cloud compute niche that I believe has interesting potential.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Parallel computation in the browser with observable webworkers</title>
        <published>2019-08-18T00:00:00+00:00</published>
        <updated>2019-08-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Zak Henry
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zakhenry.com/parallel-computation-in-the-browser-with-observable-webworkers/"/>
        <id>https://zakhenry.com/parallel-computation-in-the-browser-with-observable-webworkers/</id>
        
        <content type="html" xml:base="https://zakhenry.com/parallel-computation-in-the-browser-with-observable-webworkers/">&lt;p&gt;In the &lt;a href=&quot;&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&quot;&gt;previous article&lt;&#x2F;a&gt; we dove deep into the use of webworkers to improve the responsiveness of the main thread by offloading computation into a web worker.&lt;&#x2F;p&gt;
&lt;p&gt;To facilitate the message passing, we used the library &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cloudnc&#x2F;observable-webworker&quot;&gt;&lt;code&gt;observable-webworker&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to give us an easy to use RxJS stream-based API.&lt;&#x2F;p&gt;
&lt;p&gt;This strategy worked very well for that use case, however we aren&#x27;t quite maximising the compute resources we have available to us.&lt;&#x2F;p&gt;
&lt;p&gt;Web browsers can spin up multiple &lt;em&gt;parallel&lt;&#x2F;em&gt; web workers that will each run on different threads of the operating system. This means that any computation we do in one worker will not slow down the computation of another worker. There is a limit to this however, as not only does the computer itself have a parallelisation limit (logical processor core count) browsers will self-limit the amount of concurrency web pages are allowed to use.&lt;&#x2F;p&gt;
&lt;p&gt;The amount of parallel threads that can be run can be determined by &lt;code&gt;navigator.hardwareConcurrency&lt;&#x2F;code&gt;. This number varies depending on both underlying hardware and browser implementation. For example most desktop browser give the core count, some mobile ones will limit this value, and some browsers (Safari) won&#x27;t tell you at all.&lt;&#x2F;p&gt;
&lt;p&gt;To work out how many workers we can run in parallel, simply do &lt;code&gt;const workerCount = navigator.hardwareConcurrency - 1&lt;&#x2F;code&gt;. The &lt;code&gt;- 1&lt;&#x2F;code&gt; here is to keep a processor core spared for the main ui thread.&lt;&#x2F;p&gt;
&lt;p&gt;Put simply, with the worker pool strategy, we spin up as many workers as we are allowed, and construct a pool of work. Each worker picks up a task from the pool, executes the computation in on it&#x27;s own core, then returns the results to the main thread, grabbing the next task or shutting down if there is no more work to be done.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;To better explain this concept and how to implement it, we will build a simple application that will take a list of files (using an &lt;code&gt;&amp;lt;input type=&quot;file&quot; multiple &#x2F;&amp;gt;&lt;&#x2F;code&gt; element) and return the MD5 hash sum of those files to the main thread. This basic concept can be expanded to many different types of application, and is in itself a fairly useful application as you might want to use this strategy for hashing multiple files for upload to AWS S3 for instance.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start off by outlining exactly what we&#x27;re trying to achieve. I&#x27;ll use a little diagram syntax that is loosely based on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ReactiveX&#x2F;rxjs&#x2F;blob&#x2F;master&#x2F;docs_app&#x2F;content&#x2F;guide&#x2F;testing&#x2F;marble-testing.md&quot;&gt;RxJS marble testing&lt;&#x2F;a&gt; syntax.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-&lt;&#x2F;code&gt; idle&#x2F;waiting time&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;^&lt;&#x2F;code&gt; transferring file to worker thread&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;*&lt;&#x2F;code&gt; processing file&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;|&lt;&#x2F;code&gt; hash computed, returning results&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Take the following diagram:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;file-1.txt  ----^*******|
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the key above, we can see that we&#x27;re showing that for &lt;code&gt;file-1.txt&lt;&#x2F;code&gt; there was some idle time, then the file was picked up, processing ran for some time then the result was returned.&lt;&#x2F;p&gt;
&lt;p&gt;We can use this syntax to show what would happen if we were to compute the hashes for the files one at a time:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;file-1.txt  ^*******|
&lt;&#x2F;span&gt;&lt;span&gt;file-2.txt  ---------^***|
&lt;&#x2F;span&gt;&lt;span&gt;file-3.txt  --------------^**********|
&lt;&#x2F;span&gt;&lt;span&gt;file-4.txt  --------------------------^********|
&lt;&#x2F;span&gt;&lt;span&gt;file-5.txt  ------------------------------------^*********|
&lt;&#x2F;span&gt;&lt;span&gt;file-6.txt  -----------------------------------------------^****|
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;results     --------1----2------------3--------4----------5-----6
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ve simulated the files being different sizes&#x2F;taking different time to hash by extending the processing time (&lt;code&gt;*&lt;&#x2F;code&gt;) randomly.
The last line shows the results as they come back to the main thread, with the number just being the file number.&lt;&#x2F;p&gt;
&lt;p&gt;So from this diagram we can see that the total time to get the hash results of all 6 files is the individual durations of each file processing all added up. With a worker pool we can of course take advantage of parallel computing and run a number of these processes in parallel:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;file-1.txt  ^*******|               # worker-1
&lt;&#x2F;span&gt;&lt;span&gt;file-2.txt  ^***|                   # worker-2
&lt;&#x2F;span&gt;&lt;span&gt;file-3.txt  ^**********|            # worker-3
&lt;&#x2F;span&gt;&lt;span&gt;file-4.txt  -----^********|         # worker-2
&lt;&#x2F;span&gt;&lt;span&gt;file-5.txt  ---------^*********|    # worker-1
&lt;&#x2F;span&gt;&lt;span&gt;file-6.txt  ------------^****|      # worker-3
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;results     ----2---1---3-4--6-5
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this diagram I&#x27;ve taken the exact same processing durations from above, but shifted the processing times assuming a parallelism count of three. You can see the dramatic effect this has on the overall processing time as once the first file completes (file-2.txt) the next file (file-4.txt) is immediately picked up for processing. You can also see that even while file-3.txt is still occupying one thread, the other two threads are able to complete their unit of work and pick up a new job. One thing to note for later is that the results are returned as soon as possible, which in this case means out of order. We will need to correlate each unit of work with it&#x27;s result to marry up the data correctly.&lt;&#x2F;p&gt;
&lt;p&gt;Another way to look at the above scenario is with the same diagram syntax, but considering each worker rather than each file:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;worker-1    ^*******|^*********|    # file-1.txt, file-5.txt
&lt;&#x2F;span&gt;&lt;span&gt;worker-2    ^***|^********|         # file-2.txt, file-4.txt
&lt;&#x2F;span&gt;&lt;span&gt;worker-3    ^**********|^****|      # file-3.txt, file-6.txt
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;results     ----2---1--3--4-6-5
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The important thing to note here is that the workers are never idle - as soon as they&#x27;re finished processing work they pick up another file for processing.&lt;&#x2F;p&gt;
&lt;p&gt;Now in our example we&#x27;re only using three workers, but let&#x27;s say the client machine has 8 logical cores and the browser lets you use them, the diagram would look like the following&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;file-1.txt  ^*******|               # worker-1
&lt;&#x2F;span&gt;&lt;span&gt;file-2.txt  ^***|                   # worker-2
&lt;&#x2F;span&gt;&lt;span&gt;file-3.txt  ^**********|            # worker-3
&lt;&#x2F;span&gt;&lt;span&gt;file-4.txt  ^********|              # worker-4
&lt;&#x2F;span&gt;&lt;span&gt;file-5.txt  ^*********|             # worker-5
&lt;&#x2F;span&gt;&lt;span&gt;file-6.txt  ^****|                  # worker-6
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;results     ----26--1453
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not so interesting, but we can see that the overall time took only as long as the longest file (file-3.txt) and also that we only needed six of the seven available workers.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, we&#x27;ve talked through the theory, let&#x27;s get practical!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to use Angular here as it&#x27;s what I&#x27;m most familiar with, but the two libraries that we&#x27;ll be using (&lt;code&gt;observable-webworker&lt;&#x2F;code&gt; and &lt;code&gt;js-md5&lt;&#x2F;code&gt;) are both framework agnostic, so if you&#x27;re following along feel free to use the frontend framework you like best.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Let&#x27;s start with a quick component that can load some files from the file system and print them to the console.&lt;&#x2F;p&gt;
&lt;p&gt;Template: &lt;code&gt;files-hash.component.html&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;!-- embedme ..&#x2F;src&#x2F;app&#x2F;files-hash&#x2F;files-hash.component.html#L1-L1 --&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;multiple (change)&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hashFiles($event)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Component: &lt;code&gt;files-hash.component.ts&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;!-- embedme .&#x2F;code&#x2F;files-hash.component.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Component &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;@angular&#x2F;core&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  selector: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;app-files-hash&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  templateUrl: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;files-hash.component.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  styleUrls: [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;files-hash.component.scss&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;],
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;FilesHashComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;hashFiles&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$event&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;void {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;File[] &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$event&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.target.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running this we will see a single file select and when we select a few files we see them logged in the console in an array. Easy.&lt;&#x2F;p&gt;
&lt;p&gt;Next let&#x27;s create a webworker that will take a file as argument and return the hash of that file. We will not just return the hash string alone as we will need to correlate which hash corresponds to which file (remember from earlier we will get the results out of order).&lt;&#x2F;p&gt;
&lt;p&gt;First though, we define an interface to manage passing back this correlation of filename and hash:&lt;&#x2F;p&gt;
&lt;!-- embedme ..&#x2F;src&#x2F;app&#x2F;files-hash&#x2F;file-hasher.interface.ts --&gt;
&lt;p&gt;&lt;code&gt;file-hasher.interface.ts&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export interface &lt;&#x2F;span&gt;&lt;span&gt;FileHashPayload {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;filename&lt;&#x2F;span&gt;&lt;span&gt;: string;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;: string;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Nothing exciting here, just an interface to hold the filename and the hash.&lt;&#x2F;p&gt;
&lt;p&gt;Now for the worker. We will need both &lt;code&gt;observable-webworker&lt;&#x2F;code&gt; and &lt;code&gt;js-md5&lt;&#x2F;code&gt; to handle the workers and the hashing respectively.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;yarn add -E observable-webworker js-md5&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Worker: &lt;code&gt;file-hasher.worker.ts&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;!-- embedme ..&#x2F;src&#x2F;app&#x2F;files-hash&#x2F;file-hasher.worker.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;md5 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;js-md5&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DoWorkUnit&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ObservableWorker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;observable-webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;map &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&#x2F;operators&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;FileHashPayload &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;file-hasher.interface&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;ObservableWorker&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;WorkerPoolHashWorker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;implements &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;DoWorkUnit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;&amp;lt;File, FileHashPayload&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;workUnit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;File&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;FileHashPayload&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;readFileAsArrayBuffer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arrayBuffer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;({ filename: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.name, hash: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;md5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arrayBuffer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;) })),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;readFileAsArrayBuffer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;blob&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Blob&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;ArrayBuffer&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;observer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;blob &lt;&#x2F;span&gt;&lt;span&gt;instanceof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Blob)) {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;observer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;          &lt;&#x2F;span&gt;&lt;span&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Error(&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;`blob` must be an instance of File or Blob.&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reader &lt;&#x2F;span&gt;&lt;span&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;FileReader();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;onerror &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;observer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;onload &lt;&#x2F;span&gt;&lt;span&gt;= () &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;observer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;next&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;ArrayBuffer);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;onloadend &lt;&#x2F;span&gt;&lt;span&gt;= () &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;observer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;complete&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;readAsArrayBuffer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;blob&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;abort&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ok there&#x27;s a few things going on here, so let&#x27;s break them down.&lt;&#x2F;p&gt;
&lt;!-- embedme ..&#x2F;src&#x2F;app&#x2F;files-hash&#x2F;file-hasher.worker.ts#L7-L8 --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;ObservableWorker&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;WorkerPoolHashWorker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;implements &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;DoWorkUnit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;&amp;lt;File, FileHashPayload&amp;gt; {
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the class header we decorate it with &lt;code&gt;@ObservableWorker()&lt;&#x2F;code&gt; to register the class as a worker so that we can communicate with it from the main thread.&lt;&#x2F;p&gt;
&lt;p&gt;We also implement &lt;code&gt;DoWorkUnit&amp;lt;File, FileHashPayload&amp;gt;&lt;&#x2F;code&gt; which means that we need to implement a public method that takes a file and returns an observable of &lt;code&gt;FileHashPayload&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;!-- embedme ..&#x2F;src&#x2F;app&#x2F;files-hash&#x2F;file-hasher.worker.ts#L9-L13 --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;workUnit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;File&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;FileHashPayload&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  return this.readFileAsArrayBuffer(file).pipe(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arrayBuffer &lt;&#x2F;span&gt;&lt;span&gt;=&amp;gt; ({ filename: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;.name, hash: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;md5&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arrayBuffer&lt;&#x2F;span&gt;&lt;span&gt;) })),
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This method is pretty straightforward - we read the file as an array buffer (see the private method defined next for the implementation), then map the resulting array buffer to the &lt;code&gt;FileHashPayload&lt;&#x2F;code&gt; we defined earlier with the hash computed synchronously using the &lt;code&gt;js-md5&lt;&#x2F;code&gt; library.&lt;&#x2F;p&gt;
&lt;p&gt;Note that we&#x27;re returning a completing observable here. This is critical to the functioning of the &lt;code&gt;observable-webworker&lt;&#x2F;code&gt; library when managing thread pools. It uses the completion notification to determine that the worker has completed that unit of work and is ready for another unit of work. We return observable as it allows us to return multiple events from the worker, which is useful for outputting progress events from long running processes.&lt;&#x2F;p&gt;
&lt;p&gt;Now back to the main thread to actually implement the worker pool.&lt;&#x2F;p&gt;
&lt;!-- embedme ..&#x2F;src&#x2F;app&#x2F;files-hash&#x2F;files-hash.component.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Component &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;@angular&#x2F;core&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fromWorkerPool &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;observable-webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;FileHashPayload &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;file-hasher.interface&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  selector: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;app-files-hash&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  templateUrl: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;files-hash.component.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  styleUrls: [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;files-hash.component.scss&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;],
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;FilesHashComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;hashFiles&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$event&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;void {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;File[] &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$event&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.target.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fromWorkerPool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;&amp;lt;File, FileHashPayload&amp;gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Worker(&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;file-hasher.worker.ts&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, { type: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;subscribe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hashPayload&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;FileHashPayload&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hashed file&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hashPayload&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.filename, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hashPayload&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.hash);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is quite similar to what we were doing in earlier articles with &lt;code&gt;fromWorker()&lt;&#x2F;code&gt; but this time we use &lt;code&gt;fromWorkerPool()&lt;&#x2F;code&gt; from &lt;code&gt;observable-webworker&lt;&#x2F;code&gt;. Like &lt;code&gt;fromWorker&lt;&#x2F;code&gt; the arguments are a factory to create a worker, and the input itself.&lt;&#x2F;p&gt;
&lt;p&gt;In this example we&#x27;ve used an &lt;code&gt;Array&amp;lt;File&amp;gt;&lt;&#x2F;code&gt; as input, however the &lt;code&gt;fromWorkerPool&lt;&#x2F;code&gt; method also takes an &lt;code&gt;Observable&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; or an &lt;code&gt;Iterator&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Iterators can be very useful with this pattern as you can use their lazy evaluation feature to know &lt;em&gt;when&lt;&#x2F;em&gt; the work has been picked up for processing, not just that it has been queued.&lt;&#x2F;p&gt;
&lt;p&gt;Observable can be useful as you&#x27;re probably using observable streams anyway, and &lt;code&gt;fromWorkerPool&lt;&#x2F;code&gt; buffers the stream when the input rate exceeds the rate the worker pool can handle the work.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;If we now run our application defined above, and select some filesm, we will see something like the following:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;files (10) [File, File, File, File, File, File, File, File, File, File]
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 1-monospondylic-Notelaea-140MB.txt 8b3e48ad838ba4b3024d42fa10591c82
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 2-safekeeper-unheedful-307MB.txt d1bef08d19b30f471f161c8f5fbf9a8a
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 4-pseudochromesthesia-laryngeal-162MB.txt d77b94eea8ad01c4e4b804fe9ecd26a2
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 3-gnatcatcher-incudes-361MB.txt 30c6b4c2ea8b1dd9e5222e8ab9f2119d
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 5-amacrine-Nance-188MB.txt 7be20804dde038994a2c0e6630643046
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 8-ankaratrite-dermatoheteroplasty-149MB.txt 72cb1a511a27ac023b9960dbc3959c40
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 7-enjambed-escutcheon-256MB.txt 1f5039e50bd66b290c56684d8550c6c2
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 6-idiograph-freckly-393MB.txt bcee52113567c0040a0db2c678dfe3c3
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 9-strandage-barrelage-179MB.txt 9eab43b111f3c7ff67536d031d06f69b
&lt;&#x2F;span&gt;&lt;span&gt;Hashed file 10-refinish-mellowy-378MB.txt 212374c37a433a7b06105090002297d0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To test I&#x27;ve &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cloudnc&#x2F;observable-webworker&#x2F;blob&#x2F;master&#x2F;generate-test-files.sh&quot;&gt;generated&lt;&#x2F;a&gt; a bunch of random files with the file size in their filename.&lt;&#x2F;p&gt;
&lt;p&gt;From the output we can see that we&#x27;ve achieved the expected outcome that we theorised earlier - the files are appearing in the logs, out of order and the files that are particularly large are ordered towards the end, as we would expect. &lt;strong&gt;Success!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t just take my word for it though - I&#x27;ve created a far more interactive demo at &lt;a href=&quot;https:&#x2F;&#x2F;cloudnc.github.io&#x2F;observable-webworker&quot;&gt;https:&#x2F;&#x2F;cloudnc.github.io&#x2F;observable-webworker&lt;&#x2F;a&gt; which uses basically the exact same strategy outlined here, just with a bunch more progress messages to build a pretty timeline:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;blog-posts&#x2F;raw&#x2F;master&#x2F;posts&#x2F;observable-workers-parallel-computation-in-the-browser&#x2F;article&#x2F;webworker-pool-demo.gif&quot; alt=&quot;Webworker Pool Demo&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To try it out, jump to the &quot;Multiple Worker Pool&quot; section and select some files. Don&#x27;t worry - no files are sent out of the browser, feel free to check the source to verify or generate some random files like I did. Once you&#x27;ve selected the files, you&#x27;ll see a timeline graph of the files being processed with whatever concurrency your machine can handle.&lt;&#x2F;p&gt;
&lt;p&gt;If you inspect the sources tab in your browser devtools you will see multiple workers being created to handle the workload, and they will be shut down as soon as there is no further work in the pool.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, if you select less files than you have concurrency available, you&#x27;ll only spin up as many workers as files you&#x27;ve selected.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;That&#x27;s all for this article, I hope you&#x27;ve enjoyed it! Next up in the series we will be digging in to the performance penalties of transferring large objects to webworkers and a strategy to mitigate this cost.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;The source for this article &amp;amp; demo is available at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;blog-posts&#x2F;tree&#x2F;master&#x2F;posts&#x2F;observable-workers-parallel-computation-in-the-browser&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;blog-posts&#x2F;tree&#x2F;master&#x2F;posts&#x2F;observable-workers-parallel-computation-in-the-browser&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>TC39 Pipeline proposal comparison - RxJS case study</title>
        <published>2019-07-05T00:00:00+00:00</published>
        <updated>2019-07-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Zak Henry
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zakhenry.com/tc39-pipeline-proposal-comparison-rxjs-case-study/"/>
        <id>https://zakhenry.com/tc39-pipeline-proposal-comparison-rxjs-case-study/</id>
        
        <content type="html" xml:base="https://zakhenry.com/tc39-pipeline-proposal-comparison-rxjs-case-study/">&lt;p&gt;As you may or may not be aware there is an exciting new operator possible coming to javascript - the &lt;code&gt;|&amp;gt;&lt;&#x2F;code&gt; pipeline operator. I don&#x27;t want to re-tell what it is and how it is useful, Ben Lesh has already done an excellent job of this so read &lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;benlesh&#x2F;a-simple-explanation-of-functional-pipe-in-javascript-2hbj&quot;&gt;his article&lt;&#x2F;a&gt; first. Instead, I&#x27;m going to investigate the two competing proposals, using RxJS as a case study as this is a use-case where I&#x27;m particularly excited about the new operator.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Forewarning, this article is opinionated! If you have a differing opinion I&#x27;d love to have my mind changed as I have swung opinions on this topic a couple of times.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Before we dive into RxJS, let&#x27;s just recap first the two types and their features&#x2F;shortcomings.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;minimal-point-free-style&quot;&gt;Minimal&#x2F;Point-free Style&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;sup&gt;&lt;a href=&quot;https:&#x2F;&#x2F;babeljs.io&#x2F;en&#x2F;repl#?babili=false&amp;amp;browsers=&amp;amp;build=&amp;amp;builtIns=false&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=MYewdgzgLgBAtgQwA4wLwwGZjQPhggJwN3yIDpEkAKLASgG4AoUSWDASwBsoBTY9LCUL88wsh258aYBs3DQYBHgBMArsB5oY0gDQx2tIUSMEyStRt37ZLBezDAlcHmFjoAbiU8BqGAEYmW1gEZWUtBBIAIyEYX0imOVZFHghVbi0AbT8dACYdAGYAXUYYAB88SioI1FEYACoYHNoS8swuXgIqmLwAVmayvHN1HioqVLg9BEMa_G9xvQAGftb7Rx5nVxbRUKp85sSIEE4eMk4QAHMqJVTuBhgAenv_BaA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=false&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=true&amp;amp;presets=es2015%2Creact%2Cstage-1%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.5.0&amp;amp;externalPlugins=&quot;&gt;try it!&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arr&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;filter &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arr&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;reduce &lt;&#x2F;span&gt;&lt;span&gt;= (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;arr&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;increment &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;v &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;v &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;add &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;increment
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; 10
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this example you can see that we had to do a bit of preparatory work with our functions to make them &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Tacit_programming&quot;&gt;point-free style&lt;&#x2F;a&gt;, that is they return a function that takes one argument. Once this prep is done, the pipeline becomes a simple composition of operators. It&#x27;s worth noting at this point that this isn&#x27;t really a great example as you should probably in future still be using the &lt;code&gt;Array.prototype.*&lt;&#x2F;code&gt; methods anyway. I just picked an example that should feel familiar.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;topic-style-a-k-a-smart-mix&quot;&gt;Topic Style (a.k.a Smart Mix)&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;sup&gt;&lt;a href=&quot;https:&#x2F;&#x2F;babeljs.io&#x2F;en&#x2F;repl#?babili=false&amp;amp;browsers=&amp;amp;build=&amp;amp;builtIns=false&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=MYewdgzgLgBAlmYAnApgWxWWBeGBDGbAPnxgGoYBGAbgChRJY8ATZwmACjwBoYAjAJSESBCnzr1w0GKggBXADY4YAbUrcATNwDMAXVowAPiQDEAOjR4ADl2GkAVDA0wBB4zHMAzOEpRJbxKQkAKyuRqZmqMxywCgcHPJovHhCgXhkibwADGHuCMjomFBuIqwcJrzarpKQIAooZgogAOYcsopQAtQwAPQ9VFlAA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=false&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=true&amp;amp;presets=es2015%2Creact%2Cstage-1%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.5.0&amp;amp;externalPlugins=&quot;&gt;try it!&lt;&#x2F;a&gt; - (pick &quot;Smart&quot; in the pipeline proposal on the left)&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;increment &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;add &lt;&#x2F;span&gt;&lt;span&gt;= (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; #.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; #.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; #.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;increment
&lt;&#x2F;span&gt;&lt;span&gt; |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(#, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; 10
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Topic style takes a different approach by essentially considering the bit where you use the function as a &quot;template&quot; for its actual invocation. Because the argument is not known ahead of time, we use &lt;code&gt;#&lt;&#x2F;code&gt; as a placeholder for that value to be used once it is known.&lt;&#x2F;p&gt;
&lt;p&gt;This strategy does have the advantage that the functions used in the pipeline are simple in their declaration, at the cost that the pipeline itself is slightly more verbose.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally in my personal opinion it feels like additional cognitive load that when I do something like &lt;code&gt;add(#, 3)&lt;&#x2F;code&gt; I need to understand that I&#x27;m not really invoking the add function at all when this line of code is reached, rather I am defining a template for its execution.&lt;&#x2F;p&gt;
&lt;p&gt;With this dummy example there isn&#x27;t really a lot differentiating the two proposals, but I don&#x27;t think it is really very realistic anyway, so let&#x27;s get real!&lt;&#x2F;p&gt;
&lt;p&gt;Using RxJS we will define a pipeline which takes user input from a textbox, ignores it when character count is less than two, debounces their keystrokes, then queries an api for search results.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rxjs-pre-pipeline&quot;&gt;RxJS pre-pipeline&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fromEvent&lt;&#x2F;span&gt;&lt;span&gt;(document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;), &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;event &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;event.target.value),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText&lt;&#x2F;span&gt;&lt;span&gt;.length &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;debounce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;distinctUntilChanged&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;queryApi&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;retry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;))),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;share&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This simply implements the description given above. RxJS uses the &lt;code&gt;.pipe()&lt;&#x2F;code&gt; method in lieu of something better - Javascript pipeline operator:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rxjs-minimal-point-free-style&quot;&gt;RxJS Minimal&#x2F;Point-free Style&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fromEvent&lt;&#x2F;span&gt;&lt;span&gt;(document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;), &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;event &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;event.target.value)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText&lt;&#x2F;span&gt;&lt;span&gt;.length &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;debounce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;distinctUntilChanged&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;queryApi&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText&lt;&#x2F;span&gt;&lt;span&gt;) |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;retry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;share&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is pretty much exactly the same syntax as before, but much tidier and cleaner to read due to the new &lt;code&gt;|&amp;gt;&lt;&#x2F;code&gt; operator. How nice is that inline operator within the &lt;code&gt;switchMap&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;For comparison purposes, let&#x27;s see how the above might look with the topic-style operator implementation:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rxjs-topic-style&quot;&gt;RxJS Topic Style&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fromEvent&lt;&#x2F;span&gt;&lt;span&gt;(document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;), &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(#, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;event &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;event.target.value)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(#, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText&lt;&#x2F;span&gt;&lt;span&gt;.length &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;debounce&lt;&#x2F;span&gt;&lt;span&gt;(#, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;distinctUntilChanged
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(#, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;queryApi&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchText&lt;&#x2F;span&gt;&lt;span&gt;) |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;retry&lt;&#x2F;span&gt;&lt;span&gt;(#, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;  |&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;share
&lt;&#x2F;span&gt;&lt;span&gt;  
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Hmmm. I am not a fan. Maybe it is just a lack of familiarity talking, but the requirement that all operators need to be passing in the observable as a topic feels really unclean. Of course this would require rewriting all the RxJS operators to use this new syntax and take an observable as the first argument.&lt;&#x2F;p&gt;
&lt;p&gt;What do you think? Is there something really compelling about the topic style operator that I am missing? I know much of the discussion has been around how to handle the &lt;code&gt;await&lt;&#x2F;code&gt; keyword, but honestly I rarely use &lt;code&gt;async&#x2F;await&lt;&#x2F;code&gt; now that RxJS is mostly what I use, but that is &lt;em&gt;just my personal experience&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;On a different note, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tc39&#x2F;proposal-pipeline-operator&#x2F;issues&quot;&gt;the TC39 proposal&lt;&#x2F;a&gt; feels like it has stagnated a bit, as the Github issues are pretty much all comments from last year. Let&#x27;s kick start the discussion again and see if we can nudge it one step closer to being used!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Observable Web Workers, a deep dive into a realistic use case</title>
        <published>2019-06-30T00:00:00+00:00</published>
        <updated>2019-06-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Zak Henry
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zakhenry.com/observable-web-workers-a-deep-dive-into-a-realistic-use-case/"/>
        <id>https://zakhenry.com/observable-web-workers-a-deep-dive-into-a-realistic-use-case/</id>
        
        <content type="html" xml:base="https://zakhenry.com/observable-web-workers-a-deep-dive-into-a-realistic-use-case/">&lt;p&gt;In the &lt;a href=&quot;&#x2F;observable-webworkers-with-angular-intro&quot;&gt;previous article&lt;&#x2F;a&gt; we took a look at the library &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cloudnc&#x2F;observable-webworker&quot;&gt;&lt;code&gt;observable-webworker&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; which lets us use familiar observable patterns to construct and manage Web Workers and the communication between the threads. In this article we will develop an application, first without a web worker, then refactor it to use a web worker to demonstrate the power and usefulness of web workers. If you haven&#x27;t read the previous article, I do recommend you do so first as it goes over all the prerequisites to getting up and running, and the background of what web workers are, which I will skip over here.&lt;&#x2F;p&gt;
&lt;p&gt;This article is a deep dive for advanced developers, so get yourself a beverage and settle in. I&#x27;m assuming moderate to advanced knowledge of Typescript and RxJS in this article, so if you&#x27;re a beginner this may not be for you I&#x27;m afraid. Here&#x27;s a table of contents so you can come back to this article in stints if you like.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#brief&quot;&gt;Brief&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#data-flow-plan&quot;&gt;Data Flow Plan&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#algorithm&quot;&gt;Algorithm&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#application&quot;&gt;Application&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#common-functionality&quot;&gt;Common functionality&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#service&quot;&gt;Service&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#component&quot;&gt;Component&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#main-thread-test&quot;&gt;Main Thread Test&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#worker&quot;&gt;Worker&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#worker-thread-test&quot;&gt;Worker Thread Test&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#performance&quot;&gt;Performance&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zakhenry.com&#x2F;observable-web-workers-a-deep-dive-into-a-realistic-use-case&#x2F;#wrap-up&quot;&gt;Wrap Up&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;brief&quot;&gt;Brief&lt;&#x2F;h2&gt;
&lt;p&gt;The application we&#x27;re going to build has a few requirements, I&#x27;ll lay these out as acceptance criteria that we can refer to later.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a user I want to be able to search through text of a variety of novels fetched from &lt;a href=&quot;https:&#x2F;&#x2F;www.gutenberg.org&#x2F;&quot;&gt;Project Gutenberg&lt;&#x2F;a&gt;. My search results should contain my search phrase highlighted within the context of the matching paragraph. When I typo or misspell words I should still get expected results.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;For fun I&#x27;ll detail the criteria in the &lt;a href=&quot;https:&#x2F;&#x2F;cucumber.io&#x2F;docs&#x2F;guides&#x2F;10-minute-tutorial&#x2F;&quot;&gt;BDD Gherkin syntax&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;feature&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-feature &quot;&gt;&lt;code class=&quot;language-feature&quot; data-lang=&quot;feature&quot;&gt;&lt;span&gt;Feature: Find phrases in books by searching for them
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Background:
&lt;&#x2F;span&gt;&lt;span&gt;        Given I&amp;#39;m a user on the home page
&lt;&#x2F;span&gt;&lt;span&gt;        And I have selected the book &amp;quot;Alice in Wonderland&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        And I see a free text field in which I can type my search phrase
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    # basic search
&lt;&#x2F;span&gt;&lt;span&gt;    Scenario
&lt;&#x2F;span&gt;&lt;span&gt;        When I type &amp;quot;we’re all mad here&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        Then I should see the first search result containing the text &amp;quot;‘Oh, you can’t help that,’ said the Cat: ‘we’re all mad here. I’m mad. You’re mad.’&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    # multiple result search
&lt;&#x2F;span&gt;&lt;span&gt;    Scenario
&lt;&#x2F;span&gt;&lt;span&gt;        When I type &amp;quot;mad&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        Then I should see search results with &amp;quot;mad&amp;quot; highlighted at least &amp;quot;10&amp;quot; times
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    # search with typos
&lt;&#x2F;span&gt;&lt;span&gt;    Scenario
&lt;&#x2F;span&gt;&lt;span&gt;        When I type &amp;quot;were all madd her&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        Then I should see the first search result containing the text &amp;quot;‘Oh, you can’t help that,’ said the Cat: ‘we’re all mad here. I’m mad. You’re mad.’&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Additional to these user journey requirements, we will have the following performance requirements:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the user interface must remain responsive at all times&lt;&#x2F;li&gt;
&lt;li&gt;the user gets their search results live with every keystroke&lt;&#x2F;li&gt;
&lt;li&gt;the user can change book and the search immediate begins returning results for their previous search&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The difficult part to take particular note of in the requirements is the ability to handle typos and misspellings. This explodes the problem complexity as it is not as simple as finding a substring of a paragraph; rather we need to score candidate substrings to find the best match.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;data-flow-plan&quot;&gt;Data Flow Plan&lt;&#x2F;h2&gt;
&lt;p&gt;To start with we will put together a simple typescript file proving out the observable flow, then later build it into the application.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; playground&#x2F;observable-flow.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scan&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shareReplay&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;switchMap &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&#x2F;operators&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * First off, we create a quick enum of the books we will use as a demo. Later
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * we will update the urls to be correct
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span&gt;BookChoice {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ALICE_IN_WONDERLAND &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;some-url-to-alice-in-wonderland-text&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;SHERLOCK_HOLMES &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;some-url-to-sherlock-holmes-text&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This observable will be something like a regular dropdown, emitted whenever
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the user changes their selection.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userBookSelection$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookChoice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ALICE_IN_WONDERLAND&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This observable represents the stream of search terms the user will enter
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * into a text box. We `shareReplay(1)` so that subsequent subscribers will get
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the latest value
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userSearchTerm$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span&gt;(`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;we’re all mad here&lt;&#x2F;span&gt;&lt;span&gt;`).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;shareReplay&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This function will be responsible for fetching the content of the book given
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the enum. We&amp;#39;re cheating a little by making the enum value the url to fetch.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * For now we will just pretend the url is the content of the book.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;@todo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; implement
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getBookText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookChoice&lt;&#x2F;span&gt;&lt;span&gt;: BookChoice): Observable&amp;lt;string&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookChoice&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This function will be responsible for taking the search term and returning
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the stream of paragraphs found, as soon as they are found.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * For the purposes of quick testing, we&amp;#39;ve hardcoded as if two search results
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * were found
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * We will also likely extend this return type in future to handle highlighting
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the search phrase, but for now just the paragraph is sufficient for testing
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;@todo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; implement
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getSearchResults&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;): Observable&amp;lt;string&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm &lt;&#x2F;span&gt;&lt;span&gt;+ &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; (this will be the first result)&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm &lt;&#x2F;span&gt;&lt;span&gt;+ &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; (this will be the second result)&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  ]);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Here we take the user selected book stream and pipe it via switchMap to fetch
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the content of the book. We use switchMap() because we want to cancel the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * download of the book if the user switches to a different book to search
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * before the book has finished downloading.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Next we again switchMap() the result of the book content to the user search
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * term observable so that if the user has changed books, once it is loaded we
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * will cancel the processing of the current search term.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Next we pass that stream of search terms to getSearchResults() which will
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * itself be returning a stream of search results for that search string.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Finally, we use a scan() operator to collate the stream of search results
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * into an array so that we can present all results to the user, not just the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * most recent one
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userBookSelection$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;selection &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getBookText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;selection&lt;&#x2F;span&gt;&lt;span&gt;)),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userSearchTerm$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getSearchResults&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;scan&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults&lt;&#x2F;span&gt;&lt;span&gt;: string[], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResult&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;[...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResult&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;          }, []),
&lt;&#x2F;span&gt;&lt;span&gt;        ),
&lt;&#x2F;span&gt;&lt;span&gt;      ),
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }),
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Last but not least, to check our logic, we subscribe to the observable and
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * bind the console so we see output.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;subscribe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alrighty, we&#x27;ve got a concept of what we&#x27;re trying to build, so what do we get when we run it? I like to use &lt;code&gt;ts-node&lt;&#x2F;code&gt; for these kind of quick tests, so just run &lt;code&gt;npx ts-node --skip-project playground&#x2F;observable-flow.ts&lt;&#x2F;code&gt; (tip: the &lt;code&gt;--skip-project&lt;&#x2F;code&gt; bit is just because I&#x27;m currently in a working directory that has a tsconfig.json file that is not compatible with just running a plain nodejs script.)&lt;&#x2F;p&gt;
&lt;p&gt;Our output of the above file is as follows:&lt;&#x2F;p&gt;
&lt;!-- embedme playground&#x2F;observable-flow-out.txt --&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;[ &amp;#39;we’re all mad here (this will be the first result)&amp;#39; ]
&lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;#39;we’re all mad here (this will be the first result)&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;#39;we’re all mad here (this will be the second result)&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Okay, so this is expected - we first get one result in an array, then we get both results in the array. Success? Well kinda, but we&#x27;d really like to put some realism into this script so that we can see that our observables are having the right behavior.&lt;&#x2F;p&gt;
&lt;p&gt;So, we will now edit the script we just wrote to add a bunch of realistic delays and some more logging to see what is really going on:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; playground&#x2F;observable-flow-test.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;concatMap&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;delay&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pairwise&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scan&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shareReplay&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;startWith&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&#x2F;operators&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span&gt;BookChoice {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ALICE_IN_WONDERLAND &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;alice.text&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;SHERLOCK_HOLMES &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;sherlock.text&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This is a nice little custom operator that spaces out observables by a certain amount, this is super handy for
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * emulating user events (humans are slooow!)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;separateEmissions&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;delayTime&lt;&#x2F;span&gt;&lt;span&gt;: number) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;obs$&lt;&#x2F;span&gt;&lt;span&gt;: Observable&amp;lt;T&amp;gt;): Observable&amp;lt;T&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;obs$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;concatMap&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;v&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;v&lt;&#x2F;span&gt;&lt;span&gt;) : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;v&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;delay&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;delayTime&lt;&#x2F;span&gt;&lt;span&gt;)))),
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * For the book selection, we&amp;#39;ve piped to separateEmissions() with 4000ms
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * defined, this means when subscribed the observable will immediately emit
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Alice in Wonderland content, then 4 seconds later emit Sherlock Holmes content.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userBookSelection$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookChoice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ALICE_IN_WONDERLAND&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookChoice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;SHERLOCK_HOLMES&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;separateEmissions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Slightly different strategy for this one - we&amp;#39;re
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * 1. Piping delayed user book selection to vary the search phrase depending on
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * which book is selected
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * 2. creating a streams of individual characters
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * 3. spacing out the emissions by 100ms (this is the inter-keystroke time)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * 4. using scan to combine the previous characters
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * The result is a pretty good simulation of the user typing the phrase at 10
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * keys per second
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userSearchTerm$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userBookSelection$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;delay&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;book &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchPhrase &lt;&#x2F;span&gt;&lt;span&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;book &lt;&#x2F;span&gt;&lt;span&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookChoice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ALICE_IN_WONDERLAND
&lt;&#x2F;span&gt;&lt;span&gt;        ? `&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;we’re all mad here&lt;&#x2F;span&gt;&lt;span&gt;`
&lt;&#x2F;span&gt;&lt;span&gt;        : `&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;nothing more deceptive than an obvious fact&lt;&#x2F;span&gt;&lt;span&gt;`;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchPhrase&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;separateEmissions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;scan&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;out&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;char&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;out &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;char&lt;&#x2F;span&gt;&lt;span&gt;, &amp;#39;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;shareReplay&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Here, we&amp;#39;re guessing it will take about 200ms to download the book. We&amp;#39;ve
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * also put in a console.log so we can make sure we&amp;#39;re not going to try download
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the book on every keystroke!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookChoice
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getBookText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookChoice&lt;&#x2F;span&gt;&lt;span&gt;: BookChoice): Observable&amp;lt;string&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;getBookText called (${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookChoice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;})&lt;&#x2F;span&gt;&lt;span&gt;`);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookChoice&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;delay&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * With this function we&amp;#39;re saying that the search takes (20 milliseconds * the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * length of the search string)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This is actually totally unrealistic, but the linear variability will help
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * when understanding the logs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getSearchResults&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;): Observable&amp;lt;string&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;([&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; (first search result)&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; (second search result)&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} : ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} : ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;`),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;delay&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20 &lt;&#x2F;span&gt;&lt;span&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;.length),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;separateEmissions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This is unchanged from before
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userBookSelection$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;selection &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getBookText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;selection&lt;&#x2F;span&gt;&lt;span&gt;)),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userSearchTerm$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getSearchResults&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;scan&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults&lt;&#x2F;span&gt;&lt;span&gt;: string[], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResult&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;[...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResult&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;          }, []),
&lt;&#x2F;span&gt;&lt;span&gt;        ),
&lt;&#x2F;span&gt;&lt;span&gt;      ),
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }),
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Lastly we&amp;#39;d doing a few tricks to make the output express what happened
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * better.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * The combination of timestamp and pairwise gives us a stream of when the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * emission happened and bundles it with the previous one so we can compare
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * times to get a time taken value. The startWith(null) just gives us the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * startup time as a baseline.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Lastly we use our old friend map() to output the data in a nice format for
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * the logger.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;startWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pairwise&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;before&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tsResult&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;timeSinceLast &lt;&#x2F;span&gt;&lt;span&gt;= (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tsResult&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;timestamp &lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;before&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;) &#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} : Search Result: [${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tsResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.value.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      )}] (+${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;timeSinceLast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} seconds)&lt;&#x2F;span&gt;&lt;span&gt;`;
&lt;&#x2F;span&gt;&lt;span&gt;    }),
&lt;&#x2F;span&gt;&lt;span&gt;  )
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;subscribe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;!-- npx ts-node --skip-project playground&#x2F;observable-flow.ts &gt; playground&#x2F;observable-flow-out.txt --&gt;
&lt;!-- embedme playground&#x2F;observable-flow-test-out.txt --&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;getBookText called (http:&#x2F;&#x2F;alice.text)
&lt;&#x2F;span&gt;&lt;span&gt;0 : Search Result: [http:&#x2F;&#x2F;alice.text : w :  (first search result)] (+0.431 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;1 : Search Result: [http:&#x2F;&#x2F;alice.text : we :  (first search result)] (+0.123 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;2 : Search Result: [http:&#x2F;&#x2F;alice.text : we’ :  (first search result)] (+0.128 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;3 : Search Result: [http:&#x2F;&#x2F;alice.text : we’r :  (first search result)] (+0.121 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;4 : Search Result: [http:&#x2F;&#x2F;alice.text : we’re :  (first search result)] (+0.121 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;5 : Search Result: [http:&#x2F;&#x2F;alice.text : we’re all mad here :  (first search result)] (+1.6 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;6 : Search Result: [http:&#x2F;&#x2F;alice.text : we’re all mad here :  (first search result), http:&#x2F;&#x2F;alice.text : we’re all mad here :  (second search result)] (+0.204 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;getBookText called (http:&#x2F;&#x2F;sherlock.text)
&lt;&#x2F;span&gt;&lt;span&gt;7 : Search Result: [http:&#x2F;&#x2F;sherlock.text : n :  (first search result)] (+1.704 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;8 : Search Result: [http:&#x2F;&#x2F;sherlock.text : no :  (first search result)] (+0.125 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;9 : Search Result: [http:&#x2F;&#x2F;sherlock.text : not :  (first search result)] (+0.124 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;10 : Search Result: [http:&#x2F;&#x2F;sherlock.text : noth :  (first search result)] (+0.125 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;11 : Search Result: [http:&#x2F;&#x2F;sherlock.text : nothi :  (first search result)] (+0.122 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;12 : Search Result: [http:&#x2F;&#x2F;sherlock.text : nothing more deceptive than an obvious fact :  (first search result)] (+4.68 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;13 : Search Result: [http:&#x2F;&#x2F;sherlock.text : nothing more deceptive than an obvious fact :  (first search result), http:&#x2F;&#x2F;sherlock.text : nothing more deceptive than an obvious fact :  (second search result)] (+0.201 seconds)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alrighty let&#x27;s dig into this a bit.&lt;&#x2F;p&gt;
&lt;p&gt;We can see straight away that we fetch the Alice in Wonderland book immediately, and never again - this is perfect.&lt;&#x2F;p&gt;
&lt;p&gt;Next as the phrase begins to be typed we first get one result, then the other is appended to the results, good good.&lt;&#x2F;p&gt;
&lt;p&gt;Later on (the line starting with &lt;code&gt;5&lt;&#x2F;code&gt;) we can see that the search result slowdown has meant that we&#x27;re getting results less frequently and they are for longer search phrases than just the next character - this is what we expect as it means that the &lt;code&gt;switchMap&lt;&#x2F;code&gt; is unsubscribing from the search processor function as there is different data to be processed. This is a great opportunity to compare the different behaviors of &lt;code&gt;switchMap()&lt;&#x2F;code&gt;, &lt;code&gt;mergeMap()&lt;&#x2F;code&gt; and &lt;code&gt;exhaustMap()&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If we had chosen &lt;code&gt;mergeMap()&lt;&#x2F;code&gt;, we would see every single search result for every keystroke, but likely all overlapping with each other and would be pretty confusing. Also the overall time would be longer, assuming the CPU was saturated while processing the search.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;If we had chosen &lt;code&gt;exhaustMap()&lt;&#x2F;code&gt;, we would get the exhaustive set of results (hence the name!) in the correct order, however the overall time would be &lt;em&gt;way&lt;&#x2F;em&gt; longer as we had to wait sequentially.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In this case I think &lt;code&gt;switchMap()&lt;&#x2F;code&gt; is the correct behavior as the user is not interested in interim search results before they have finished typing, and we gain efficiency by immediately cancelling computation of irrelevant search results.&lt;&#x2F;p&gt;
&lt;p&gt;Back to the output analysis, in the line marked &lt;code&gt;7&lt;&#x2F;code&gt; we see we&#x27;ve switched to fetching Sherlock Holmes with the new search phrase. Success!&lt;&#x2F;p&gt;
&lt;p&gt;Okay, we&#x27;re in a state now where we&#x27;re pretty confident in the general data flow, let&#x27;s cover the algorithm we will use to score paragraphs, then later start building the application.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;algorithm&quot;&gt;Algorithm&lt;&#x2F;h2&gt;
&lt;p&gt;Given the requirement that our search terms may contain typos and misspellings, the algorithm to find the best paragraph to show the user is non trivial. There are a number of different fuzzy string matching algorithms, each with their own strengths &amp;amp; weaknesses. Given our requirement is about typos and misspellings, not word order mistakes, computing the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Levenshtein_distance&quot;&gt;Levenshtein distance&lt;&#x2F;a&gt; sounds like a great candidate.&lt;&#x2F;p&gt;
&lt;p&gt;Levenshtein distance is the number of changes required to change one string to another. The changes can be either insertions, deletions or substitutions. The distance is simply the minimum count of changes.&lt;&#x2F;p&gt;
&lt;p&gt;As an example the distance between &lt;code&gt;hello&lt;&#x2F;code&gt; and &lt;code&gt;helo&lt;&#x2F;code&gt; is &lt;code&gt;1&lt;&#x2F;code&gt; because there is one insertion required. Two identical strings will have a distance of &lt;code&gt;0&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now our inputs don&#x27;t quite suit this algorithm as we&#x27;re comparing a small search phrase with a large paragraph - our distance scoring will mostly indicate the difference in length of the search term and the paragraph. Instead we will modify the algorithm to ignore the start and end of the search string such that we compute the minimum Levenshtein distance of the search string when placed anywhere in the paragraph.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we will normalize Levenshtein distance against the length of the search string in order to compute a similarity score. This will be used to rank the paragraphs by best match.&lt;&#x2F;p&gt;
&lt;p&gt;In order to not make this article any longer than it already is, I&#x27;m going to skip over the implementation, but you can dig in to it yourself at https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;blog-posts&#x2F;tree&#x2F;master&#x2F;posts&#x2F;observable-workers-deep-dive&#x2F;src&#x2F;app&#x2F;book-search&#x2F;common&#x2F;fuzzy-substring.ts&lt;&#x2F;p&gt;
&lt;p&gt;What is important to know is that there is a function &lt;code&gt;fuzzySubstringSimilarity&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;common&#x2F;fuzzy-substring.ts#L70-L73
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fuzzySubstringSimilarity&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;needle&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;haystack&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;): FuzzyMatchSimilarity {
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;which returns &lt;code&gt;FuzzyMatchSimilarity&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;common&#x2F;fuzzy-substring.ts#L1-L9
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export interface &lt;&#x2F;span&gt;&lt;span&gt;FuzzyMatch {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;substringDistance&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;startIndex&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;endIndex&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export interface &lt;&#x2F;span&gt;&lt;span&gt;FuzzyMatchSimilarity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;FuzzyMatch &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;similarityScore&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We will be using this return type to both score the paragraphs of the novel and return the start and end index of the match so we can highlight it in the output.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s test the algorithm against the user story defined above:&lt;&#x2F;p&gt;
&lt;!-- embedme playground&#x2F;fuzzy-substring-test.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fuzzySubstringSimilarity &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;src&#x2F;app&#x2F;book-search&#x2F;common&#x2F;fuzzy-substring&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;similarity &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fuzzySubstringSimilarity&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;were all madd her&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;‘Oh, you can’t help that,’ said the Cat: ‘we’re all mad here. I’m mad. You’re mad.’&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Similarity: &lt;&#x2F;span&gt;&lt;span&gt;`, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;similarity&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All we&#x27;re doing here is defining the needle as the search string, and the haystack as the expected matching sentence. If we run this file we get the following:&lt;&#x2F;p&gt;
&lt;!-- npx ts-node --skip-project playground&#x2F;fuzzy-substring-test.ts &gt; playground&#x2F;fuzzy-substring-test-out.txt --&gt;
&lt;!-- embedme playground&#x2F;fuzzy-substring-test-out.txt --&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;Similarity:  {
&lt;&#x2F;span&gt;&lt;span&gt;  substringDistance: 2,
&lt;&#x2F;span&gt;&lt;span&gt;  startIndex: 42,
&lt;&#x2F;span&gt;&lt;span&gt;  endIndex: 59,
&lt;&#x2F;span&gt;&lt;span&gt;  similarityScore: 0.8823529411764706
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As expected, we see a substringDistance of &lt;code&gt;2&lt;&#x2F;code&gt; (to get to the expected substring we need to add a &lt;code&gt;&#x27;&lt;&#x2F;code&gt; and remove a &lt;code&gt;d&lt;&#x2F;code&gt;, two changes, distance is &lt;code&gt;2&lt;&#x2F;code&gt;). We can also see that the &lt;code&gt;similarityScore&lt;&#x2F;code&gt; is very high (range is 0-1) so we can expect this search to score highly on this paragraph against other paragraphs.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, we now have a algorithm sorted, and a data flow to manage it. Let&#x27;s build an app!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;application&quot;&gt;Application&lt;&#x2F;h2&gt;
&lt;p&gt;So far we&#x27;ve just been working with single typescript files, to work through our ideas, but now we&#x27;re going to jump into using a framework as this is much more realistic to our real world problems we hope to solve by understanding this article.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to use Angular, but if you prefer any of the other awesome frameworks don&#x27;t fret as this is going to be fairly framework agnostic anyway. If you&#x27;ve made it this far, it&#x27;s probably safe to assume you have the basics of framework development down pat so I&#x27;ll gloss over that.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;ng new observable-workers-deep-dive
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;ng generate component book-search
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ok, we&#x27;ve got a basic framework scaffolded, given we&#x27;re trying to test the difference between using observables directly, and using observables with web workers, as we build out the application we will try to extract the common functionality into one place. That way as many variables as possible are controlled during our performance tests as the worker and main thread strategies will be using the exact same core algorithm code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;common-functionality&quot;&gt;Common functionality&lt;&#x2F;h3&gt;
&lt;p&gt;To start with we will build a utility function to handle getting a stream of fuzzy-match scored paragraphs:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;common&#x2F;book-search.utils.ts#L8-L25
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;SearchMatch {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph&lt;&#x2F;span&gt;&lt;span&gt;: string;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphNumber&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;: FuzzyMatchSimilarity;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getSearchResults&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphs&lt;&#x2F;span&gt;&lt;span&gt;: string[],
&lt;&#x2F;span&gt;&lt;span&gt;): Observable&amp;lt;SearchMatch&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphs&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;observeOn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;asyncScheduler&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;index&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fuzzySubstringSimilarity&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph&lt;&#x2F;span&gt;&lt;span&gt;, paragraphNumber: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;index &lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;    }),
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some very important things to note here. We&#x27;re converting the array of paragraphs to an observable stream of individual paragraphs with &lt;code&gt;from()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After this we use &lt;code&gt;observeOn(asyncScheduler)&lt;&#x2F;code&gt; - this is &lt;em&gt;critical&lt;&#x2F;em&gt; to the responsiveness of the application. Basically what this is doing is rescheduling the emissions of the &lt;code&gt;from()&lt;&#x2F;code&gt; observable from being synchronous to asynchronous. This allows subscribers to our stream to disconnect from the paragraph stream &lt;strong&gt;without having to compute the fuzzy substring scores for the entire book&lt;&#x2F;strong&gt;. This will allow us to discard partial search results when the search string becomes invalid due to the user typing more characters.&lt;&#x2F;p&gt;
&lt;p&gt;Finally we &lt;code&gt;map()&lt;&#x2F;code&gt; to the computation function, and attach the paragraph index number to the output - we will later use this to work out how far along the computation is as a percentage.&lt;&#x2F;p&gt;
&lt;p&gt;Next up for our common functions is an accumulation function that will&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;take our stream of search results&lt;&#x2F;li&gt;
&lt;li&gt;collate them into an array&lt;&#x2F;li&gt;
&lt;li&gt;order the array of results by score&lt;&#x2F;li&gt;
&lt;li&gt;take the top ten results&lt;&#x2F;li&gt;
&lt;li&gt;slice up the resulting paragraph into non matching text and matching text&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;common&#x2F;book-search.utils.ts#L27-L77
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export interface &lt;&#x2F;span&gt;&lt;span&gt;MatchingParagraph {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;before&lt;&#x2F;span&gt;&lt;span&gt;: string;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;: string;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;after&lt;&#x2F;span&gt;&lt;span&gt;: string;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;score&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export interface &lt;&#x2F;span&gt;&lt;span&gt;SearchResults {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphs&lt;&#x2F;span&gt;&lt;span&gt;: MatchingParagraph[];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchedParagraphCount&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphCount&lt;&#x2F;span&gt;&lt;span&gt;: number;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;accumulateResults&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphCount&lt;&#x2F;span&gt;&lt;span&gt;: number) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;obs$&lt;&#x2F;span&gt;&lt;span&gt;: Observable&amp;lt;SearchMatch&amp;gt;): Observable&amp;lt;SearchResults&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;obs$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;scan&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults&lt;&#x2F;span&gt;&lt;span&gt;: SearchMatch[], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResult&lt;&#x2F;span&gt;&lt;span&gt;: SearchMatch) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResult&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      }, []),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;startWith&lt;&#x2F;span&gt;&lt;span&gt;([]),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatches&lt;&#x2F;span&gt;&lt;span&gt;: SearchMatch[]): SearchResults &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;last &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatches&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatches&lt;&#x2F;span&gt;&lt;span&gt;.length - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            searchedParagraphCount: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;last &lt;&#x2F;span&gt;&lt;span&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;last&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphNumber &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphCount&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            paragraphs: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatches
&lt;&#x2F;span&gt;&lt;span&gt;              .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;similarityScore &lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;similarityScore&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;              )
&lt;&#x2F;span&gt;&lt;span&gt;              .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;              .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph &lt;&#x2F;span&gt;&lt;span&gt;}) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                  score: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;similarityScore&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                  match: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;substring&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;startIndex&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;endIndex&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                  ),
&lt;&#x2F;span&gt;&lt;span&gt;                  before: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;substring&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;startIndex&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;                  after: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraph&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;substring&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchMatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;endIndex&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;                };
&lt;&#x2F;span&gt;&lt;span&gt;              }),
&lt;&#x2F;span&gt;&lt;span&gt;          };
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;      ),
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Despite the line count, there is nothing particularly complex in here, some points to note however:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;scan()&lt;&#x2F;code&gt; operator is used so that we get results streamed as they come in, if we wanted to wait for completion of the search we would use &lt;code&gt;reduce()&lt;&#x2F;code&gt; (the rxjs operator, not &lt;code&gt;Array.prototype.reduce&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;We use &lt;code&gt;startWith([])&lt;&#x2F;code&gt; so that a subscriber immediately gets an empty result set on subscription&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Lastly we combine these two functions into a single one that accepts a search term and a book, and returns the accumulated stream of search results.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;common&#x2F;book-search.utils.ts#L79-L87
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getAccumulatedSearchResults&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span&gt;: string,
&lt;&#x2F;span&gt;&lt;span&gt;): Observable&amp;lt;SearchResults&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphs &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n\n&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getSearchResults&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphs&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;accumulateResults&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphs&lt;&#x2F;span&gt;&lt;span&gt;.length),
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;service&quot;&gt;Service&lt;&#x2F;h3&gt;
&lt;p&gt;Ok, basic functions that do the heavy lifting are all sorted, we&#x27;re pretty much on the home stretch! Let&#x27;s create a service to wrap this logic that we will later use in our component.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;main-thread&#x2F;book-search.service.ts#L11-L44
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Injectable&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  providedIn: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;root&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;BookSearchService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;HttpClient&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;search&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookSelection$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;BookChoice&amp;gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;SearchResults&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;processSearch&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookSelection$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;auditTime&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; emit results at a maximum of 60fps
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;share&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;protected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;processSearch&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;search$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;SearchResults&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sharedSearchTerm$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;search$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;shareReplay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;));
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, { responseType: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;})),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sharedSearchTerm$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getAccumulatedSearchResults&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;          }),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      }),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this service, we inject an &lt;code&gt;HttpClient&lt;&#x2F;code&gt; into the constructor; this allows us to fetch the book content. Next is a public method that we will use in our component. It takes two streams:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the first is the &lt;code&gt;BookChoice&lt;&#x2F;code&gt;. Recall from earlier this is a string enum with the values being the URl of the book&lt;&#x2F;li&gt;
&lt;li&gt;the second is the search phrase itself that is used for fuzzy matching against the paragraphs of the book&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We call out to a local &lt;code&gt;processSearch&lt;&#x2F;code&gt; method which simply takes the url and pipes it to a &lt;code&gt;switchMap()&lt;&#x2F;code&gt; that fetches the content, then in turn pipes to the search term observable and finally we switch on that observable to call out to &lt;code&gt;getAccumulatedSearchResults&lt;&#x2F;code&gt; that we built earlier.&lt;&#x2F;p&gt;
&lt;p&gt;Back in the &lt;code&gt;search()&lt;&#x2F;code&gt; method, we pipe the results from &lt;code&gt;processSearch()&lt;&#x2F;code&gt; and first use the &lt;code&gt;auditTime()&lt;&#x2F;code&gt; operator. This restricts the data output rate to 60 frames per second otherwise we could potentially get tens of thousands of &lt;code&gt;SearchResults&lt;&#x2F;code&gt; per second, which would completely oversaturate the change detection strategy of the framework when we try to display the results in the DOM.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly we add a &lt;code&gt;share()&lt;&#x2F;code&gt; to the search results as we don&#x27;t want multiple subscribers to trigger computation of the search results more than once.&lt;&#x2F;p&gt;
&lt;p&gt;Next let&#x27;s define the component logic to connect this service to user input.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;component&quot;&gt;Component&lt;&#x2F;h3&gt;
&lt;p&gt;Our component requirements are super simple - it needs to provide a form control for both the book selection and the search term, then output the results of the search.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;main-thread&#x2F;book-search.component.ts#L13-L59
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  selector: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;app-book-search&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  templateUrl: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;book-search.component.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  styleUrls: [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;book-search.component.css&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;],
&lt;&#x2F;span&gt;&lt;span&gt;  changeDetection: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ChangeDetectionStrategy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;OnPush&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;BookSearchComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;componentName &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Main thread search&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookChoices &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;[
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      url: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookChoice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ALICE_IN_WONDERLAND&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      name: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Alice in Wonderland&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      url: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookChoice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;SHERLOCK_HOLMES&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      name: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Sherlock Holmes&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  ];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookSelectionFormControl &lt;&#x2F;span&gt;&lt;span&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;FormControl(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userBookSelection$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;BookChoice&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookSelectionFormControl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;valueChanges&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTermFormControl &lt;&#x2F;span&gt;&lt;span&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;FormControl(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userSearchTerm$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTermFormControl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;valueChanges&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    SearchResults
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookSearchHandler&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;search&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userBookSelection$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userSearchTerm$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResultParagraphs$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    MatchingParagraph[]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;));
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResultProgress$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    [number, number]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchResults$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchedParagraphCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paragraphCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;]),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookSearchHandler&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;BookSearchService&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Points of interest in this component:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;we construct a new &lt;code&gt;FormControl&lt;&#x2F;code&gt; for the book selection, then immediately set up an observable that observes the &lt;code&gt;valueChanges&lt;&#x2F;code&gt; of that control&lt;&#x2F;li&gt;
&lt;li&gt;we do the same thing for the search terms&lt;&#x2F;li&gt;
&lt;li&gt;next we construct the &lt;code&gt;private searchResults$: Observable&amp;lt;SearchResults&amp;gt;&lt;&#x2F;code&gt; using the previous two observables, it calls out to the service that we just defined earlier&lt;&#x2F;li&gt;
&lt;li&gt;the last two public members observe the &lt;code&gt;searchResults$&lt;&#x2F;code&gt;, the first extracting the matching paragraphs, the second extracting the search progress information&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Next let&#x27;s take a look how this will be displayed in the template:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- src&#x2F;app&#x2F;book-search&#x2F;main-thread&#x2F;book-search.component.html --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;{{ componentName }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;[formControl]&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;bookSelectionFormControl&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;*ngFor&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;let book of bookChoices&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;[value]&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;book.url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    {{ book.name }}
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;[formControl]&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;searchTermFormControl&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;span &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;*ngIf&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;searchResultProgress$ | async as searchResultProgress&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  Progress: {{ searchResultProgress[0] }} &#x2F; {{ searchResultProgress[1] }} ({{
&lt;&#x2F;span&gt;&lt;span&gt;    searchResultProgress[0] &#x2F; searchResultProgress[1] | percent
&lt;&#x2F;span&gt;&lt;span&gt;  }})
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ng-container &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;*ngFor&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;let matchingParagraph of searchResultParagraphs$ | async&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hr &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;blockquote&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;{{ matchingParagraph.before }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strong&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;{{ matchingParagraph.match }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strong&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;{{ matchingParagraph.after }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Score: {{ matchingParagraph.score }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;blockquote&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ng-container&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Nice simple template, we&#x27;ve got&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a basic &lt;code&gt;&amp;lt;select&amp;gt;&lt;&#x2F;code&gt; to manage the book selection&lt;&#x2F;li&gt;
&lt;li&gt;then an &lt;code&gt;&amp;lt;input&amp;gt;&lt;&#x2F;code&gt; to manage the user search&lt;&#x2F;li&gt;
&lt;li&gt;then a &lt;code&gt;&amp;lt;span&amp;gt;&lt;&#x2F;code&gt; that outputs the search result progress&lt;&#x2F;li&gt;
&lt;li&gt;lastly a repeated &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;&#x2F;code&gt; to output the results of the search&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note that we&#x27;re using &lt;code&gt;AsyncPipe&lt;&#x2F;code&gt; to managed &lt;em&gt;all&lt;&#x2F;em&gt; subscriptions - this greatly simplifies the component logic, and allows us to use &lt;code&gt;OnPush&lt;&#x2F;code&gt; change detection strategy as the &lt;code&gt;AsyncPipe&lt;&#x2F;code&gt; manages marking the component to be checked.&lt;&#x2F;p&gt;
&lt;p&gt;At long last, we&#x27;re there, let us test it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;main-thread-test&quot;&gt;Main Thread Test&lt;&#x2F;h3&gt;
&lt;p&gt;Ok so when we run the app, we&#x27;re presented with the two controls. So let&#x27;s select &quot;Alice and Wonderland&quot; and type that typo-ridden string &lt;code&gt;&quot;were all madd her&quot;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;media.giphy.com&#x2F;media&#x2F;WQrOW2zcL4mxlF6odx&#x2F;giphy.gif&quot; alt=&quot;Main thread Demo&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Okay! This is good - we&#x27;ve managed to fetch search results, and the first result is the expected match:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;were all madd her
&lt;&#x2F;span&gt;&lt;span&gt; Progress: 900 &#x2F; 901 (100%)
&lt;&#x2F;span&gt;&lt;span&gt;‘Oh, you can’t help that,’ said the Cat: ‘we’re all mad here. I’m mad. You’re mad.’
&lt;&#x2F;span&gt;&lt;span&gt;Score: 0.8823529411764706
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;However&lt;&#x2F;strong&gt; the responsiveness of the application was awful - that gif is not stuttering, it really did lock up after typing the first character then all the results appeared after a few seconds.
Fortunately we have the incredible tool that is Web Workers up our sleeves, and we can use one to free up this oversaturated main thread to just do UI stuff, and keep our expensive search result computation in a separate thread.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;worker&quot;&gt;Worker&lt;&#x2F;h3&gt;
&lt;p&gt;So, let&#x27;s create a worker&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;ng generate web-worker book-search
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will put a &lt;code&gt;book-search.worker.ts&lt;&#x2F;code&gt; file into our application:&lt;&#x2F;p&gt;
&lt;!-- embedme playground&#x2F;example.worker.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reference &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, ({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span&gt;}) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span&gt;= `&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;worker response to ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;`;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;postMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We don&#x27;t want any of that boilerplate, so we will delete it and implement our own worker version of the &lt;code&gt;processSearch&lt;&#x2F;code&gt; method we created in the service earlier:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;worker-thread&#x2F;book-search.worker.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DoWork&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ObservableWorker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;observable-webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ajax &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&#x2F;ajax&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;distinctUntilChanged&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shareReplay&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&#x2F;operators&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;getAccumulatedSearchResults&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;SearchResults&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WorkerInput&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;common&#x2F;book-search.utils&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;ObservableWorker&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;BookSearchWorker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;implements &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;DoWork&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;&amp;lt;WorkerInput, SearchResults&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;WorkerInput&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;SearchResults&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;distinctUntilChanged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm$ &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;distinctUntilChanged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;shareReplay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;ajax&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, responseType: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;})),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;switchMap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;getAccumulatedSearchResults&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bookText&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;          ),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      }),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This ought to look pretty familiar, as it is really just the &lt;code&gt;processSearch&lt;&#x2F;code&gt; function within a &lt;code&gt;@ObservableWorker()&lt;&#x2F;code&gt;- decorated class from the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cloudnc&#x2F;observable-webworker&quot;&gt;&lt;code&gt;observable-webworker&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; package. The only difference is that the input to the &lt;code&gt;work()&lt;&#x2F;code&gt; method is a single stream of &lt;code&gt;Observable&amp;lt;WorkerInput&amp;gt;&lt;&#x2F;code&gt; which we split and observe separately.&lt;&#x2F;p&gt;
&lt;p&gt;From there it is essentially identical, though we do use the rxjs &lt;code&gt;ajax()&lt;&#x2F;code&gt; method rather than &lt;code&gt;HttpClient&lt;&#x2F;code&gt; as the &lt;code&gt;Injector&lt;&#x2F;code&gt; is not available in the worker context.&lt;&#x2F;p&gt;
&lt;p&gt;Now for the main thread class to manage this worker:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;worker-thread&#x2F;book-search-worker.service.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Injectable &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;@angular&#x2F;core&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fromWorker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;observable-webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;combineLatest&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;map &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&#x2F;operators&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookSearchService &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;main-thread&#x2F;book-search.service&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;SearchResults&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WorkerInput &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;common&#x2F;book-search.utils&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Injectable&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  providedIn: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;root&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;BookSearchWorkerService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;BookSearchService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;protected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;processSearch&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;search$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;SearchResults&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;WorkerInput&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;combineLatest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;search$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;searchTerm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;})),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fromWorker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Worker(&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;book-search.worker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, { type: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Because we&#x27;ve already implemented the &lt;code&gt;search()&lt;&#x2F;code&gt; logic in &lt;code&gt;BookSearchService&lt;&#x2F;code&gt; we simply extend that class and override the implementation of &lt;code&gt;processSearch()&lt;&#x2F;code&gt; in a new &lt;code&gt;BookSearchWorkerService&lt;&#x2F;code&gt; class.&lt;&#x2F;p&gt;
&lt;p&gt;In a real situation you probably would just replace the &lt;code&gt;processSearch&lt;&#x2F;code&gt; method, but we&#x27;re going to have two implementations in this article so we can compare them side-by-side later.&lt;&#x2F;p&gt;
&lt;p&gt;Our implementation of &lt;code&gt;processSearch&lt;&#x2F;code&gt; is super simple, we &lt;code&gt;combineLatest&lt;&#x2F;code&gt; the two input streams into the form that the Worker required. Then we use the &lt;code&gt;fromWorker()&lt;&#x2F;code&gt; method from &lt;code&gt;&#x27;observable-webworker&#x27;&lt;&#x2F;code&gt; to wrap the construction of a &lt;code&gt;new Worker&lt;&#x2F;code&gt; and pass the input stream as the second argument.&lt;&#x2F;p&gt;
&lt;p&gt;Now, while we&#x27;re trying to preserve the old behaviour, we need a new component to show off this shiny new worker backed service:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; src&#x2F;app&#x2F;book-search&#x2F;worker-thread&#x2F;book-search-worker.component.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ChangeDetectionStrategy&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Component &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;@angular&#x2F;core&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookSearchWorkerService &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;book-search-worker.service&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookSearchComponent &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;main-thread&#x2F;book-search.component&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookSearchService &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;main-thread&#x2F;book-search.service&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  selector: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;app-book-search-worker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  templateUrl: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;main-thread&#x2F;book-search.component.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  styleUrls: [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;..&#x2F;main-thread&#x2F;book-search.component.css&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;],
&lt;&#x2F;span&gt;&lt;span&gt;  changeDetection: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ChangeDetectionStrategy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;OnPush&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  providers: [
&lt;&#x2F;span&gt;&lt;span&gt;    { provide: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookSearchService&lt;&#x2F;span&gt;&lt;span&gt;, useClass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BookSearchWorkerService &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;BookSearchWorkerComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;BookSearchComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;componentName &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Worker thread search&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not much going on here! All we&#x27;re doing is extending the &lt;code&gt;BookSearchComponent&lt;&#x2F;code&gt;, reusing it&#x27;s template and styles, and providing our new &lt;code&gt;BookSearchWorkerService&lt;&#x2F;code&gt; instead of the default &lt;code&gt;BookSearchService&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now in our &lt;code&gt;app.component.html&lt;&#x2F;code&gt; template we can insert this new component:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- src&#x2F;app&#x2F;app.component.html --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;app-book-search&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;app-book-search&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;app-book-search-worker&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;app-book-search-worker&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So now we have our two implementations of book search - one in the main thread, the other backed by a worker thread service.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;worker-thread-test&quot;&gt;Worker Thread Test&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s test the new worker component!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;media.giphy.com&#x2F;media&#x2F;XAyCxNHS30wkyHcKq9&#x2F;giphy.gif&quot; alt=&quot;Worker thread Demo&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Wow! How&#x27;s that for performance?! Now I wouldn&#x27;t call this the nicest of user experiences, but you can really see the power of web workers in action here.&lt;&#x2F;p&gt;
&lt;p&gt;Also in this demo you can really see the reason we had to &lt;code&gt;auditTime()&lt;&#x2F;code&gt; the search results as the DOM update frequency here is super high. In fact, if you comment out that function, you will find that the web worker implementation performance is significantly &lt;strong&gt;worse&lt;&#x2F;strong&gt; as it is emitting search results at such a high frequency that it causes a backpressure of change detection cycles for Angular, making the overall performance actually worse. This was a real takeaway lesson for me, and hopefully you too -&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Web Workers can make your computation output so fast that displaying the results in the DOM can become a major overall bottleneck&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Visual comparisons are all well and good, but let&#x27;s properly look into how the performance differs between the two strategies&lt;&#x2F;p&gt;
&lt;h2 id=&quot;performance&quot;&gt;Performance&lt;&#x2F;h2&gt;
&lt;p&gt;To test the relative performance I will search the phrase &lt;code&gt;&quot;There is nothing more deceptive than an obvious fact&quot;&lt;&#x2F;code&gt; within the Sherlock Holmes book. I&#x27;ll do it three times using the regular main thread strategy, clearing the input each time, then the same for the web worker strategy.&lt;&#x2F;p&gt;
&lt;!--![Strategy Performance](playground&#x2F;performance-test.png)--&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;thepracticaldev.s3.amazonaws.com&#x2F;i&#x2F;rso3qkd37i1bvqtvd4ct.png&quot; alt=&quot;Strategy Performance&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;From these graphs it is clear the difference using a web worker makes - the main thread (in yellow in this graph) holds at 100% for a full 15 seconds, during which repainting will suffer greatly. With the web worker strategy, the main thread barely peaks at 50% utilisation, and is actually mostly idle for the duration.&lt;&#x2F;p&gt;
&lt;p&gt;It is worth noting at this time that the overall duration is roughly the same, and it would be expected that in some circumstances the worker strategy might be slightly slower overall. This is because the strategy requires spinning up a new thread, and structured copying of the data flowing between threads is not zero-cost.&lt;&#x2F;p&gt;
&lt;p&gt;One other consideration is that we&#x27;re not fully utilising the power of web workers - the computation job that we&#x27;re running can easily be broken up into smaller tasks for &lt;em&gt;multiple&lt;&#x2F;em&gt; workers, and processed using a thread pool strategy. This is a topic for another article, and I might just be covering that one next.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap Up&lt;&#x2F;h2&gt;
&lt;p&gt;You&#x27;ve reached the end! This was a bit of a marathon I&#x27;m afraid but there was an awful lot of content to cover. In summary, we outlined a real world scenario where users want to robustly do full text search over a novel, we then came up with a data flow strategy, then an algorithm, then built it out into an application, then refactored to use an observable web worker strategy, and finally did some performance metrics to prove the utility of web workers. Phew!&lt;&#x2F;p&gt;
&lt;p&gt;The code is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;blog-posts&#x2F;tree&#x2F;master&#x2F;posts&#x2F;observable-workers-deep-dive&quot;&gt;all available on Github&lt;&#x2F;a&gt; so please feel free to clone it and have a play. If you spot any errors or room for improvement please do raise an issue or a pull request, I&#x27;m learning too!&lt;&#x2F;p&gt;
&lt;p&gt;Next in this series, I will be either demonstrating how to further improve the performance of this application using thread pools, or implementing a new real world use case that does intensive image manipulation which will demonstrate high performance techniques of passing large data between threads. Let me know if you have a preference!&lt;&#x2F;p&gt;
&lt;p&gt;Thanks again for staying with me here, I hope that this article has been useful to you.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Observable Web Workers with Angular - Introduction</title>
        <published>2019-06-26T00:00:00+00:00</published>
        <updated>2019-06-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Zak Henry
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zakhenry.com/observable-webworkers-with-angular-intro/"/>
        <id>https://zakhenry.com/observable-webworkers-with-angular-intro/</id>
        
        <content type="html" xml:base="https://zakhenry.com/observable-webworkers-with-angular-intro/">&lt;h2 id=&quot;tl-dr&quot;&gt;tl;dr&lt;&#x2F;h2&gt;
&lt;p&gt;Web Workers are awesome and Angular CLI now supports them natively.&lt;br &#x2F;&gt;
Unfortunately, the Web Worker API is not Angular-like and therefore, I&#x27;m introducing a library &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;observable-webworker&quot;&gt;&lt;code&gt;observable-webworker&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you already know what web workers are and how to use them in Angular, feel free to &lt;!--[skip ahead](#implementing-observablewebworker)--&gt; where I will show you how to refactor the initial worker generated by the CLI into a superpowered worker with &lt;code&gt;observable-webworker&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;context&quot;&gt;Context&lt;&#x2F;h2&gt;
&lt;p&gt;In this article series we will explore using &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Web_Workers_API&#x2F;Using_web_workers&quot;&gt;Web Workers&lt;&#x2F;a&gt; to offload heavy computation to another thread. Keeping the main thread free for interaction with the user is important. We don&#x27;t typically think of threading very often in the frontend world, as non-blocking APIs and the event loop model of Javascript typically allows us to have execution of user interaction events &lt;em&gt;in-between&lt;&#x2F;em&gt; longer running processes such as waiting for an HTTP request to resolve.&lt;&#x2F;p&gt;
&lt;p&gt;Because of this, for the most part, you don&#x27;t need threading or web workers for most tasks. However, there are a set of problems that do require heavy computation that would ordinarily block the main thread, and therefore user interaction (or modification of the DOM). This issue can manifest itself in stuttering animations, unresponsive inputs, buttons that appear not to work immediately etc.&lt;&#x2F;p&gt;
&lt;p&gt;Often the answer to this has been to run intensive computations server side, then send the result back to the browser once done. This solution does have a real cost however - you need to manage a server API, monetarily computation itself isn&#x27;t free (you&#x27;re paying for the server) and there may be a significant latency issue if you need to interact with the computation frequently.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, &lt;code&gt;Web Workers&lt;&#x2F;code&gt; are the solution to all this - they allow you to schedule units of work in browser that run &lt;em&gt;in parallel&lt;&#x2F;em&gt; to the main execution context, then once done can pass their result back to the main thread for rendering in the DOM etc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-your-first-web-worker-in-angular&quot;&gt;Creating your first Web Worker in Angular&lt;&#x2F;h2&gt;
&lt;p&gt;The worker API is relatively simple - in Typescript you can create a new worker with just&lt;&#x2F;p&gt;
&lt;!-- embedme src&#x2F;readme&#x2F;basic-worker.ts --&gt; 
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myWorker &lt;&#x2F;span&gt;&lt;span&gt;= new Worker(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;worker.js&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is all very well, however we love to use Typescript, and be able to import other files etc. Being stuck with a single javascript file is not very scalable.&lt;&#x2F;p&gt;
&lt;p&gt;Previously working with workers (heh) in Angular was fairly painful as bundling them requires custom Webpack configuration. However, Angular CLI version 8 brings built-in support for properly compiling &amp;amp; bundling web workers.&lt;&#x2F;p&gt;
&lt;p&gt;The docs are at https:&#x2F;&#x2F;angular.io&#x2F;guide&#x2F;web-worker but we will step through everything required (and more!) here.&lt;&#x2F;p&gt;
&lt;p&gt;Before we get going, we&#x27;ve set up an empty Angular CLI project with&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ng&lt;&#x2F;span&gt;&lt;span&gt; new observable-workers
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next we need to enable web worker support, so we run&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;ng generate web-worker
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;re prompted for a name, so we will call it demo.&lt;&#x2F;p&gt;
&lt;p&gt;Angular CLI will now update &lt;code&gt;tsconfig.app.json&lt;&#x2F;code&gt; and &lt;code&gt;angular.json&lt;&#x2F;code&gt; to enable web worker support, and create us a new &lt;code&gt;demo.worker.ts&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;!-- embedme src&#x2F;readme&#x2F;initial-angular-cli-worker&#x2F;demo.worker.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;reference &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, ({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span&gt;}) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span&gt;= `&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;worker response to ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;`;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;postMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To get this worker running, lets update our &lt;code&gt;AppComponent&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;!-- embedme src&#x2F;readme&#x2F;initial-angular-cli-worker&#x2F;app.component.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Component &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;@angular&#x2F;core&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  selector: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;app-root&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  templateUrl: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;app.component.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  styleUrls: [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;app.component.scss&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;]
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;AppComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;runWorker&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;demoWorker &lt;&#x2F;span&gt;&lt;span&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Worker(&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;demo.worker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, { type: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;demoWorker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;onmessage &lt;&#x2F;span&gt;&lt;span&gt;= (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Got message&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.data);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;demoWorker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;postMessage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And the template:&lt;&#x2F;p&gt;
&lt;!-- embedme src&#x2F;readme&#x2F;initial-angular-cli-worker&#x2F;app.component.html --&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;(click)&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;runWorker()&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;Run Worker&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Okay, so now we&#x27;re all good to go, run the application if you aren&#x27;t already, and you&#x27;ll be greeted with a single button in the DOM. Are you excited yet?&lt;&#x2F;p&gt;
&lt;p&gt;Click that sucker, and you will see in the dev console&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Got message worker response to hello
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yeehaw, we got a response from a separate thread, and &lt;strong&gt;Angular compiled the worker for us&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Also, to prove this is actually a worker, if you go to the &lt;code&gt;Sources&lt;&#x2F;code&gt; tab in Chrome, you will see that there is a worker running there as &lt;code&gt;1.worker.js&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The fact that we can still see it running is important - it means that we have not destroyed the worker, despite receiving the only message we will get back from it. If we click the button again, we will construct a brand new worker, and the old one will continue to hang around. This is a bad idea! We should always clean up after we&#x27;re done with a worker.&lt;&#x2F;p&gt;
&lt;p&gt;Before we worry about how to destroy a worker, let&#x27;s reflect for a bit on the API that we have with workers so far - we need to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;construct the worker&lt;&#x2F;li&gt;
&lt;li&gt;declare a property and assign a function to it in order to get the response back from the worker&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;addEventListener&lt;&#x2F;code&gt; within the worker itself, we have to&lt;&#x2F;li&gt;
&lt;li&gt;call a global &lt;code&gt;postMessage&lt;&#x2F;code&gt; function to send back information to the main thread&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This API doesn&#x27;t feel very Angular-like does it? We&#x27;re used to deal with clean hook-based APIs and have fallen in love with RxJS for dealing with streams of data and asynchronous responses, why can&#x27;t we have this for Web Workers too?&lt;&#x2F;p&gt;
&lt;p&gt;Well, we can. And this happens to be the whole point of this article. I&#x27;d like to introduce a new library &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;observable-webworker&quot;&gt;&lt;code&gt;observable-webworker&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; which seeks to address this clunky API and give us a familiar experience we&#x27;re used to with Angular.&lt;&#x2F;p&gt;
&lt;p&gt;I should note that this library doesn&#x27;t actually depend on Angular at all, and will work beautifully with React or any other framework or lack thereof! The only dependency is a &lt;code&gt;peerDependency&lt;&#x2F;code&gt; on RxJS.&lt;&#x2F;p&gt;
&lt;p&gt;To best introduce the concepts of the library, we will refactor our current web worker to use &lt;code&gt;observable-webworker&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implementing-observable-webworker&quot;&gt;Implementing observable-webworker&lt;&#x2F;h2&gt;
&lt;p&gt;To start, we will install &lt;code&gt;observable-webworker&lt;&#x2F;code&gt;. I&#x27;m using Yarn, but you know how to install packages right?!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;yarn&lt;&#x2F;span&gt;&lt;span&gt; add&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -E&lt;&#x2F;span&gt;&lt;span&gt; observable-webworker
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First of all, we&#x27;ll update the &lt;code&gt;AppComponent&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
 &lt;!-- embedme src&#x2F;readme&#x2F;observable-webworker-implementation&#x2F;app.component.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Component &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;@angular&#x2F;core&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fromWorker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;observable-webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  selector: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;app-root&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  templateUrl: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;app.component.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;  styleUrls: [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;app.component.scss&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;]
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;AppComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;runWorker&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fromWorker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;&amp;lt;string, string&amp;gt;(&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Worker(&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;demo.worker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, { type: &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;subscribe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;message &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Got message&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note we&#x27;ve imported &lt;code&gt;fromWorker&lt;&#x2F;code&gt; from &lt;code&gt;observable-webworker&lt;&#x2F;code&gt;. The first argument is a worker &lt;em&gt;factory&lt;&#x2F;em&gt; - we need to have the ability to lazily construct a worker on-demand, so we pass a factory and the library can construct it when needed. Also Webpack needs to find the &lt;code&gt;new Worker(&#x27;.&#x2F;path&#x2F;to&#x2F;file.worker&#x27;)&lt;&#x2F;code&gt; in the code in order to be able to bundle it for us.
The second argument &lt;code&gt;input$&lt;&#x2F;code&gt; is a simple stream of messages that will go to the worker. The generics (&lt;code&gt;&amp;lt;string, string&amp;gt;&lt;&#x2F;code&gt;) that we pass to the worker indicate the input and output types. In our case the input is a very simple &lt;code&gt;of(&#x27;hello&#x27;)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now for the worker:&lt;&#x2F;p&gt;
&lt;!-- embedme src&#x2F;readme&#x2F;observable-webworker-implementation&#x2F;demo.worker.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DoWork&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ObservableWorker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;observable-webworker&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Observable &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;map &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rxjs&#x2F;operators&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;ObservableWorker&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;DemoWorker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;implements &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;DoWork&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;&amp;lt;string, string&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;Observable&amp;lt;string&amp;gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;worker response to ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;    );
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can see we have completely restructured the worker to use an API that is far more familiar to Angular developers - we use a decorator &lt;code&gt;@ObservableWorker()&lt;&#x2F;code&gt; which lets the library bootstrap and work its magic, and we implement the &lt;code&gt;DoWork&lt;&#x2F;code&gt; interface, which requires us to create a &lt;code&gt;public work(input$: Observable&amp;lt;string&amp;gt;): Observable&amp;lt;string&amp;gt;;&lt;&#x2F;code&gt; method.&lt;&#x2F;p&gt;
&lt;p&gt;The actual content of the method is super simple. We process the observable stream with a simple &lt;code&gt;map&lt;&#x2F;code&gt;, which is returned from the method. Behind the scenes the library will call &lt;code&gt;postMessage&lt;&#x2F;code&gt; to get the data back to the main thread.&lt;&#x2F;p&gt;
&lt;p&gt;You can now run the application again and it will work exactly as before, with the one exception that the worker will automatically be terminated as no more work needs to be done.&lt;&#x2F;p&gt;
&lt;p&gt;As we&#x27;re now using observables, the library is able to know when subscribers have finished listening and can clean up the worker appropriately.&lt;&#x2F;p&gt;
&lt;p&gt;This wraps up Part One of this series. In later articles we will discuss a more real-world implementation of workers, how to deal with errors, and how to work with very large object passing between main and worker threads. Possibly I will also do a deep dive into the implementation of observable-webworker as it uses some of the more exotic RxJS operators like &lt;code&gt;materialize()&lt;&#x2F;code&gt; and &lt;code&gt;dematerialize()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;All of these features are available with &lt;code&gt;observable-webworker&lt;&#x2F;code&gt; now, so I encourage you to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cloudnc&#x2F;observable-webworker#observablewebworker&quot;&gt;check out the readme&lt;&#x2F;a&gt;, as it goes into more detail about the features than I do here in this article.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;This is my first blog post ever! I&#x27;d love to hear from you if you have any feedback at all, good or bad!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ensuring accuracy of README code snippets</title>
        <published>2019-06-20T00:00:00+00:00</published>
        <updated>2019-06-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Zak Henry
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zakhenry.com/ensuring-accuracy-of-readme-code-snippets/"/>
        <id>https://zakhenry.com/ensuring-accuracy-of-readme-code-snippets/</id>
        
        <content type="html" xml:base="https://zakhenry.com/ensuring-accuracy-of-readme-code-snippets/">&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;&#x2F;h2&gt;
&lt;p&gt;It is the first interaction a developer will have with your library. They hear about the library through various means, but they will at some point all end up at the README. It is critical that their first impression is good, and nothing can turn a developer off a library faster than the example code blocks in the README not actually working.&lt;&#x2F;p&gt;
&lt;p&gt;Markdown itself doesn&#x27;t do much to help us - it is a simple markup language that knows nothing about the libraries that we&#x27;re building, and it certainly can&#x27;t compile the code within the code snippets that we create.&lt;&#x2F;p&gt;
&lt;p&gt;Generally, this leaves us with two options:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;We write the code directly in markdown and cross our fingers that it will be okay&lt;&#x2F;li&gt;
&lt;li&gt;We write the code in a separate file, that we can test against the library and we copy-paste into the markdown document&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;With both options, there is a huge amount of room for mistakes. In the first case, our code might not even be valid! In the second, there is a number of files to remember to update and then remember to copy paste the content.&lt;&#x2F;p&gt;
&lt;p&gt;In both instances, we may later on introduce a breaking change to the library, and completely forget to update the docs and then we&#x27;re in the worst case scenario - the documentation does not reflect the library.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve contributed to open source a bit, and have managed to run into all these cases and while they do get picked up eventually, it is fairly embarrassing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;&#x2F;h2&gt;
&lt;p&gt;Becoming fed up with this annoyance, I decided to automate the process of managing updating the README from source code snippets. I&#x27;ve built a little nodejs commandline utility which takes all the hassle out of maintaining a README (and other markdown documents too!).&lt;&#x2F;p&gt;
&lt;p&gt;Assuming you have nodejs installed you can simply run&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;npx embedme README.md
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now to begin with you&#x27;re probably not going to get very useful output; you need to annotate your code blocks such that &lt;code&gt;embedme&lt;&#x2F;code&gt; can find the appropriate file to insert&lt;&#x2F;p&gt;
&lt;p&gt;So, let us step through an example:&lt;&#x2F;p&gt;
&lt;p&gt;Say I have the following README.md:&lt;&#x2F;p&gt;
&lt;!--![Starting README](images&#x2F;starting-readme.png)--&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;thepracticaldev.s3.amazonaws.com&#x2F;i&#x2F;uoxlzyidms7p10xpqomz.png&quot; alt=&quot;Starting README&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What you can do with &lt;code&gt;embedme&lt;&#x2F;code&gt; is insert a comment in the first line of the code block:&lt;&#x2F;p&gt;
&lt;!-- ![Commented README](images&#x2F;commented-readme.png) --&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;thepracticaldev.s3.amazonaws.com&#x2F;i&#x2F;8odzyzsuyis00jqyxth1.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Next, if you now run the following:&lt;&#x2F;p&gt;
&lt;!--![Embedme Output](images&#x2F;embedme-output-4-lines.png)--&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;thepracticaldev.s3.amazonaws.com&#x2F;i&#x2F;7ngzgcbls8n5l8ynm759.png&quot; alt=&quot;Embedme Output&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You will see output from the library indicating where it found code blocks and what it did with them (i.e. in this case, inserted the content from &lt;code&gt;path&#x2F;to&#x2F;example-usage.ts&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Next, to prove the utility of the library, make a change to your source code&lt;&#x2F;p&gt;
&lt;!-- embedme path&#x2F;to&#x2F;example-usage.ts --&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hello &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;my-amazing-library&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;An update here!&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;hello&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; outputs Hello World!
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then rerun &lt;code&gt;embedme&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;!-- ![Embedme Output](images&#x2F;embedme-output-5-lines.png) --&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;thepracticaldev.s3.amazonaws.com&#x2F;i&#x2F;t3x278265yvs2dlffdcx.png&quot; alt=&quot;Embedme Output&quot; &#x2F;&gt;
You will see your readme file has updated:&lt;&#x2F;p&gt;
&lt;!-- ![Updated README](images&#x2F;updated-readme.png) --&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;thepracticaldev.s3.amazonaws.com&#x2F;i&#x2F;xnc97apz4qqfqi3ykf2w.png&quot; alt=&quot;Updated README&quot; &#x2F;&gt;
So, as you&#x27;ve now seen, we&#x27;re in a situation where out example code is outside of the README, but with a simple utility we can have it be up to date. Not only that, but we can (and should!) unit test this example code against our library, which will ensure that if we cause breaking changes to our library, we are guaranteed that the documentation will be kept up to date with correct code snippets.&lt;&#x2F;p&gt;
&lt;p&gt;Embedme has a number of other features, including...&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;extracting specific lines (&lt;code&gt;&#x2F;&#x2F;path&#x2F;to&#x2F;your&#x2F;file.ts#L10-L20&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--verify&lt;&#x2F;code&gt; flag which doesn&#x27;t actually write to file, but checks there are not changes (good for CI scripts!)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--stdout&lt;&#x2F;code&gt; flag to output the change to be stdout in order to be redirected to another file&lt;&#x2F;li&gt;
&lt;li&gt;using markdown comment syntax to define code blocks without being required to insert the comment within the code block. (&lt;code&gt;&amp;lt;!-- embedme path&#x2F;to&#x2F;your&#x2F;file.ts --&amp;gt;&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;and a few other handy features...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I encourage you to view the library for more info - https:&#x2F;&#x2F;github.com&#x2F;zakhenry&#x2F;embedme and add it to your automated workflow to get documentation that is both a pleasure to write, and guaranteed to be valid.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</content>
        
    </entry>
</feed>
