Photo by Victoire Joncheray on Unsplash

RxJS: Cache and AJAX Race Conditions

RxJS’s `startWith` has a Secret Use Case

I created a pretty cool method to use both a cached and AJAX version of data at the same time.

The Problem

When working with data stored in a cache, you want to make sure that data is up-to-date. There are quite a few ways to solve this dilemma, but the way I’ve proposed is to first retrieve the cache version, then retrieve the most recent version. This way, your application is snappy from cache, but the real data can still load in under-the-hood.

To prevent any strange UI problems, another important piece is to only pass the AJAX data through if it updated. No matter the implementation, this means you’ll need to store the cached version in state somewhere so it’s readily available to compare against the returned AJAX data.

Way of the Promise

The first time I tried something like this was 3 years ago for a 24 hour coding challenge at work. Thanks to the genius of an architect who helped me think through the problem, I was able to get a working prototype done in 6 hours. Still, it was a pretty tough problem to solve.

At the time, all I knew were callbacks and promises. As you probably know, promises only emit values once, so I wrapped the jQuery promise implementation we had with my own promise handler logic and create a custom promise that emitted two values. This wasn’t visible to anyone consuming it though. you’d just create a CachedDataFetcher object and go from there.

Since I don’t have the exact code with me nor do I want to reveal company secrets, I’m going to rewrite the API from memory.

It looked something like this:

jQuery’s promise implementation wasn’t the same as the official ECMAScript standard which is why this API has a done and error method.

The create function was part of a factory pattern which returned new CachedDataFetcher() with whatever options you’d passed in. Everything else is similar to what you’d expect, except the done method triggers the promise to grab data instead of when fetch is called.

Rewriting this today, here’s my simplified take on that implementation:

I purposefully didn’t add the error-handling logic so it’d be easier to follow. The real implementation was a lot more complicated.

The API is so-so, and the example and real implementations are pretty terrible, but I hope you can see where I was coming from.

As long as you returned something that was API-compatible with normal promises, you could easily switch-out existing data calls in your codebase with the CachedDataFetcher , and it would only require minimal changes. From everyone else’s perspective, this was a normal promise; but it had the benefit of being able to emit two values.

Now that I’ve been on the observable train for a couple years, it makes a lot more sense to use an observable for emitting both a cached and AJAX version of data, but at the time, I was working with what I knew. That’s a bit of a lie though. I used observables with Knockout too, but as they’re tied with the DOM, the thought never crossed my mind to try using them for this particular purpose.

Observables Over Time

These days, I’d run this logic through a Redux-Observable epic and trigger it off a Redux action. Instead, I’m going to show you how I solved this same issue last night using straight RxJS with subjects and the amazing startWith operator. Talk about hidden potential!

We can’t use race because I want both values. The only other way would be using merge , but doing that, it’s possible the AJAX response could come back first before the cached response. You don’t want to override good data with bad data.

This is how I ended up utilizing startWith to mimic the cache then AJAX behavior:

As you can see, it’s many lines fewer than jerry-rigging a promise and much easier to reason-about, but how does it work?

Once we have the value we want out of cache, we start our ajax observable. While the AJAX call happens immediately when our switchMap occurs, startWith runs first before anything passes through the pipeline. This gives us time to prime the local state in distinctUntilChanged and pass on the cached value to our subscriber if it exists while keeping our AJAX logic simple.

distinctUntilChanged won’t let values pass unless they’re different from the previous one that passed. That handles our “don’t update from AJAX if it’s the same as cache” requirement.

In this case, our subscriber is a subject. Instead of doing store.dispatch under-the-hood, we’re calling subject.next by passing in the subject itemList$ into the subscribe method.

You can mimic the same functionality as Redux-Observable this way, but why write your own implementation if one already exists that does all this for you? I had a quick project that wasn’t very large so I did it on my own, but generally, I’d use an established library with good documentation so future devs (including me) can maintain it.

Cache and AJAX Over Time

Although this solves the original issue from 3 years ago, it’s only part of last night’s problem. I need it to update the item list value over time. That means my AJAX call needs to happen on an interval, while my cache value only happens once.

Using the same code as before, the solution is a simple as adding interval :

The placement of startWith matters, and we’ve used it twice I the same pipeline. That’s the trick.

As you probably know, interval is like timer where it only fires after the timer runs out. In this case, it would wait 1 hour before making the AJAX call. Using startWith , we can fire off our AJAX call immediately on subscribe as well as pass our cached value through the pipeline at the exact same time!

I think startWith is pretty fantastic. There’s also endWith which provides similar functionality when an observable completes. It’s a really nifty trick and really slimmed down this solution in an intuitive way.

More Reads

If you’ve got an interest in more topics related to RxJS and Redux-Observable, you should checkout my other articles: