I recently joined a cool software startup, where we are building an equipment management software. Part of the application is a page called MapTrac, which is a single-page, auto-reloading map view of the equipment that a client manages. The data this page uses is constantly updating in the background, so it is difficult to make it a fast and smooth experience for end users. It is also tricky to keep fetching new data without stopping the rest of the application from running.
To help solve these problems, we use a cool, open-source library called Redux-Saga. Obviously, this library relies on Redux to work (it is a Redux middleware). As I said, I’m fairly new to the company, so I don’t fully understand the library myself, which is why I am writing this post. Hopefully by the end I understand it better!
First things first, what is Redux? From what I understand, Redux is a tool that helps keep your application, specifically the data it uses, reliable. The philosophy behind Redux is that your application has a single source of “truth”â€Š–â€Šthat is, your app’s state. This state (or store, in Redux-speak) is read-only, which means that it can only be updated by calling an action. Actions are basically objects that tell your app that something happened: the user clicked a button, data was received, or anything else that might affect the state of your app.
The action, in turn, calls a reducer, which is a pure function. If you aren’t sure what a pure function is, you can go here to learn more. Basically, it is a function that doesn’t update anything outside of its scope. For a reducer, it means that it creates a new state, based on what the action tells it, rather than updating the old one. It then returns the new state, which replaces the old state. This might seem tedious, but it helps your application behave consistently, and maintain control of your state. Redux forces you to go through this process so that you can always easily know why your state has changed. There are other reasons to use Redux, but that’s the main one: if you are worried about having a large, complicated, frequently-updating data store, then Redux can help you manage it.
It isn’t all rainbows and unicorns in Redux-land. While Redux goes a long way in reducing side-effects that can affect your state, asynchronous tasks like getting data from your server can still be a pain with plain Redux. Sagas is a Redux middleware that makes handling those cases easier, and more pure. We’ll get to how it does that in a bit, but first, let’s go over the basic concepts behind asynchronous programming.
Often, this subject is confused with the idea of threads, and while they are interconnected, they are not the same. Both synchronous and asynchronous processes can be run in a single or multiple threads. For a visual example, see this excellent StackOverflow answer. Another response on that StackOverflow question provides a helpful metaphor to remember the difference between asynchronous and synchronous tasks:
You are in a queue to get a movie ticket. You cannot get one until everybody in front of you gets one, and the same applies to the people queued behind you.
You are in a restaurant with many other people. You order your food. Other people can also order their food, they don’t have to wait for your food to be cooked and served to you before they can order. In the kitchen restaurant workers are continuously cooking, serving, and taking orders. People will get their food served as soon as it is cooked.
Good question! As I mentioned before, Sagas is used to make it easier to manage asynchronous (independent) tasks, especially when it comes to their effect on the state of your application. While there are benefits to using asynchronous tasks (such as removing costly I/O blocking calls), they can be notoriously difficult to manage. They are less predictable than synchronous tasks, so you can’t depend on them to return a value in time for other blocks of code to use.
Sagas separate the different kinds of code, which allows you to benefit from the asynchronous tasks while not blocking the rest of your application from running. In effect, Sagas runs on its own thread, which keeps the two blocks of code from interfering with each other. As an example, consider an application that needs to grab data from a database every time a specific Redux action is called. Using only synchronous code, the application would come to a halt every time that action is called. Using Sagas, it is possible to make these database calls in the background, while the application continues to run. The data is fetched, and it updates the state, without interrupting the application’s flow.
This is just one example of a “side effect that Sagas can help with. Remember, a side effect happens when a procedure affects something outside it’s scope. Other long-running or “impure tasks (such as interactions with browser cache) may be improved by moving them into a Saga.
Sagas is a powerful tool, and like all tools, you should carefully consider whether or not it is the right one for your use case. Some of these considerations include:
- Will it simplify your code?
- Will it improve the experience for the end user?
- Does your application rely on asynchronous tasks?
- Is introducing Sagas worth the additional complexity?
While we use Sagas at our company, it doesn’t make sense in every situation. In fact, we recently reconsidered the worth we were getting from using the technology. Although we determined that it currently makes sense to continue to rely on it, that may not always be the case. You shouldn’t use a technology just because it can give you some benefit. You should weigh the pros and cons, and use Sagas if the benefits outweigh the drawbacks, subtracting the opportunity cost of the time, effort, and money it will take to implement it. Here are a few of the pros and cons, as I see it:
- Can significantly speed up application, especially one that relies on fetching new data regularly (such as in single-page applications).
- Separates UI components from business logic, which can make your code more organized and understandable.
- Sagas are great for doing automated testing because they use generator functions.
- Despite what I wrote above, Sagas introduces a lot of complexity, which can hinder understanding.
- Just because you are doing it in the background, you can still slow down your application with too many requests.
Sagas can be a very useful tool, if you know how to use it. It can help you and your organization leverage new technologies to make your application better organized, more easily maintained, and a smoother experience for the end user. To continue to learn more, below are some useful links. Also, I’m still learning, so if I got anything wrong, or I missed something important, please let me know! I’d love to get some feedback.
Thanks for reading!