As an engineer, you spend a lot of time with APIs — you’re either building APIs for others or consuming other’s APIs. Working with APIs therefore is as much an art as it is science. One of the most common mistakes that engineers make is not thinking enough about performance from the ground up. We want to make things work first and deal with performance later. That’s perfectly fine, but it helps to be aware of it while building that v1 so you can avoid chaos later. If you’ve done this long enough, I’m sure you know that initially, everything works well but over time things become slow.

It gets even tricky when we’re consuming others’ APIs which we don’t have much control on. Often, many APIs come back within milliseconds initially, but over time, as complexity increases, they start to become slow. Some user action could trigger an expensive query in an API we’re consuming and things come to a stall. APIs don’t come with a performance expectation or guarantee, so when consuming APIs, it helps to be aware of what kind of things can slow them down, especially in the critical paths of your project.

Let’s take a use case where one or two user actions are sharing a single view area to display their responses. If one of the APIs connected to these actions takes a long time, then we could have a situation where the view gets jumbled if it’s not handled properly.

For example, google `amzn stock` and you’ll see something like below: one single stock trend chart controlled by user selected time range controls like 1 day, 5 day, 1 month etc.

As we switch between the time periods, the view area will reflect the change. Let’s assume that your api call to fetch 1 year trend chart got worse and the user clicked on it and it takes forever to load and the user lost patience and switched to a lesser time period say 3 months and got the chart loaded in no time. While they are viewing the 3 months chart, the API call that you fired to fetch the 1 year data will come back with it’s response and redraw the chart with 1 year data.

This is not a complicated problem to solve. You can just look at the current active time period and ignore the response that arrived late. But as you build your v1, you may not think you need this handling while developing your UI because all of your APIs returned immediately when you developed it. You may not have anticipated that your API’s can slow down in certain scenarios or over time.

Now, if such scenario affects the state of a common shared service in your application that’s serving many components, the situation can get a lot worse, and often, try to find out the root cause in such situations can become very complicated.

Let’s see how we could solve this. An easy way is to maintain a flag in that common shared service that tracks the current backend API we’re expecting a response from. But what if we called the same API multiple times? The flag won’t work. We could extend the flag to store the ‘state’ of each backend API call, but that can get messy and complicated — every time we store state, we’re taking a big risk because we need to “remember” to keep that state updated as the API changes and that almost always leads to undesirable bugs down the road.

The less “global” state you maintain, the better. It not only helps keep your code simple and modular, but it also gives you more freedom to increase concurrency.

A better approach in this case is to not deal with the response coming out of a slow API, but to simply stop receiving responses from it. Simply terminate the APIs you no longer care about a response for and move on. You can easily do that by keep track of all in-progress API calls, and when you need to fire a new one, simply terminate the older ones you no longer need and move on.

If you’re using jQuery ajax method then keep the reference to the XMLHttpRequest returned by the jQuery ajax method and invoke abort method in your flow at the right time.

If you’re using ES6 promises then sorry that just won’t work — you cannot terminate an in-progress API call attached to a promise. More about promises here.

Welcome to the RxJS world! RxJS tries to bring order to the chaos of API performance with a fully asynchronous, event driven model. If something takes time, let it take time. Let’s run with whatever we have. I’m not here to debate whether it’s good or bad or whether it’s the best approach, but I find it handy to deal with these types of situations.

In this case, you can simply use a switch construct on your observerables and get to the latest asynchronous event that you are interested in without worrying about any state maintenance or terminating previous in-progress APIs. When you use the right constructs, this library does all these actions internally for you so you don’t have to worry about them!

Checkout plunker as an example:

In this example, a slow API is mimicked with an Observable.timer. The RxJs subject instance asyncActionSubject created as part of the shared service constructor, uses the switch construct to simply switch to the latest observable returned. Rest all taken care by the Rxjs framework. Check out the RxJs github page here for more details.

Key Takeaways:

When consuming APIs, always think about what scenario can cause it to slow down. You can get a sense for this based on what the API is doing, how much and what type of data it returns, and what can happen if the simple case you are testing with can get complex. This is especially important if your code path is servicing user actions with lots of filters and selectors, for example. Always think about the big picture — how will users interact with your code, which in turn affects the APIs you are consuming? Step back and think about what can go wrong and handle those cases from the ground up. Always try to be as decentralized and stateless as you can. Centralization and statefulness are enemies for debugging and concurrency. You will save a LOT of headaches later down the road. Switch your thinking about asynchronous calls . Get out of dealing with a chain of response handlers with RxJs patterns. Understand how they work in your case and what happens when the calls return in a time sequence. Last but not least, less is always better. Ask for the minimum amount of data you need, and actually consume the minimum amount of data you want even if an API returns more.

Thanks to Amy Tsai for helping with this post.