Key Points
- The single-threaded nature of JavaScript means that asynchronous operations, such as API calls, are critical to non-blocking UIs, and efficient handling of these operations is critical in React Redux applications.
- Redux Thunk, Redux-Saga, and Redux-Observables are popular Redux asynchronous operation management middleware, each providing different advantages based on the complexity and needs of the application.
- Redux Thunk simplifies asynchronous action distribution by allowing return functions instead of action, enabling sequential API calls and processing related data in these functions.
- Redux-Saga leverages ES6 generators to provide more powerful solutions for complex scenarios such as handling race conditions, pauses, and cancel operations, making it suitable for large applications.
- Redux-Observables leverages RxJS to provide a powerful way to manage side effects through action flows, providing excellent scalability and control over asynchronous operations in complex application architectures.
This article was originally published in Codebrahma.
JavaScript is a single-threaded programming language. That is to say, when you write the following code...
…The second line will only be executed after the first line is executed. This won't be a problem in most cases, because the client or server performs millions of calculations per second. We only notice these effects when we perform costly calculations (a task that takes quite a while to complete – a network request takes some time to return).
Why am I only showing API calls (network requests) here? What about other asynchronous operations? API calls are a very simple and useful example to describe how to handle asynchronous operations. There are other operations, such as setTimeout()
, performance-intensive computing, image loading, and any event-driven operations.
When building an application, we need to consider how asynchronous execution affects the structure. For example, think of fetch()
as a function that performs an API call (network request) from the browser. (Ignore whether it is an AJAX request. Just treat its behavior as asynchronous or synchronous.) The time elapsed when the request is processed on the server does not occur on the main thread. Therefore, your JS code will continue to execute and once the request returns a response, it will update the thread.
Consider this code:
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
In this case, since fetch()
is asynchronous, when we try to get userDetails
we will not have userId
. So we need to build it in a way that ensures that the second row is executed only after the first row returns the response.
Most modern network request implementations are asynchronous. But this doesn't always work because we rely on previous API response data for subsequent API calls. Let's see how to build it specifically in a ReactJS/Redux application.
React is a front-end library for creating user interfaces. Redux is a state container that manages the entire state of an application. Using React with Redux allows us to create efficient and scalable applications. In such a React application, there are several ways to build asynchronous operations. For each method, we will discuss its advantages and disadvantages in terms of the following factors:
- Code clarity
- Scalability
- Easy to handle errors
For each method, we will execute these two API calls:
1. Get userDetails
city from
(first API response)
Suppose the endpoint is /details
. It will include the city in the response. The response will be an object:
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
2. Based on the user city, we will get all restaurants in city
Suppose the endpoint is. The response will be an array: /restuarants/:city
userDetails: { … city: 'city', … };Remember that we can only make the second request after the first request is completed (because it depends on the first request). Let's take a look at various methods:
- Use Promise or async/await directly with
-
setState
Using Redux Thunk - Using Redux-Saga
- Using Redux Observables
redux-async, redux-promise, redux-async-queue).
PromisesPromise is an object that may produce a single value at some time in the future: a parsed value or an unresolved cause (for example, a network error occurred). — Eric ElliotIn our example, we will use the axios library to get the data, which returns a promise when we make a network request. The Promise may parse and return a response or throw an error. So once the
React component is mounted, we can get it directly like this:
['restaurant1', 'restaurant2', …]In this way, when the state changes (due to the acquisition), the
component will automatically re-render and load the restaurant list.
Async/await is a new implementation that we can use to perform asynchronous operations. For example, the same function can be achieved by:
componentDidMount() { axios.get('/details') // 获取用户详细信息 .then(response => { const userCity = response.city; axios.get(`/restaurants/${userCity}`) .then(restaurantResponse => { this.setState({ listOfRestaurants: restaurantResponse, // 设置状态 }) }) }) }Both methods are the easiest. Since the whole logic is inside the component, we can easily get all the data at once after the component is loaded.
Disadvantages of this method
The problem arises when complex data-based interactions occur. For example, consider the following situation:
- We do not want the thread executing JS to be blocked by network requests.
- All the above situations make the code very complex and difficult to maintain and test.
- Also, scalability will be a big problem because if we plan to change the flow of the application, we need to remove all the fetch operations from the component.
- Imagine what we would do if the component was on top of the parent-child tree. Then we need to change all the representation components that depend on the data.
- Also note that the entire business logic is inside the component.
How do we improve?
-
Status Management Using global storage can actually solve half of our problems in these cases. We will use Redux as our global storage.
-
Move business logic to the right place If we consider moving business logic out of the component, where exactly can we do this? In actions? In reducers? Through middleware? Redux's architecture is synchronous. Once you distribute an action (JS object) and it reaches the storage, the reducer will operate on it.
-
Make sure there is a separate thread that executes the asynchronous code and that any changes to the global state can be retrieved by subscription
From this we can learn that if we move all the acquisition logic before the reducer—i.e., action or middleware—then we can distribute the correct action at the right time. For example, once the fetch starts, we can distribute dispatch({ type: 'FETCH_STARTED' })
, and when it is done, we can distribute dispatch({ type: 'FETCH_SUCCESS' })
.
Want to develop a React JS application?
Using Redux Thunk
Redux Thunk is a middleware for Redux. It basically allows us to return functions instead of objects as action. This helps by providing dispatch
and getState
as parameters of the function. We use dispatch
to distribute necessary actions at the right time. The benefits are:
- Allow multiple distributions within the function
- The association of business logic with the acquisition will be moved from the React component to the actions.
In our example, we can rewrite the action like this:
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
As you can see, we now have a good control over what type of action is distributed. Each function call (such as fetchStarted()
, fetchUserDetailsSuccess()
, fetchRestaurantsSuccess()
, and fetchError()
) will distribute an action of type normal JavaScript object, and additional details can be added if needed. So now the task of reducer is to process each action and update the view. I didn't discuss reducer because it's simple from here and the implementation may be different.
To make this work, we need to connect the React component to Redux and bind the action to the component using the Redux library. Once done, we can simply call this.props.getRestaurants()
, which in turn will handle all the above tasks and update the view according to the reducer.
For its scalability, Redux Thunk can be used for applications that do not involve complex control of asynchronous actions. Additionally, it works seamlessly with other libraries, as described in the topic in the following section.
However, it is still a bit difficult to perform certain tasks using Redux Thunk. For example, we need to pause the intermediate fetch operation, or only allow the latest calls when there are multiple such calls, or if other APIs get this data and we need to cancel.
We can still implement these, but it will be more complicated to execute accurately. Compared with other libraries, the code clarity of complex tasks will be slightly worse and will be more difficult to maintain.
Using Redux-Saga
With the Redux-Saga middleware, we can gain the additional advantage to address most of the above features. Redux-Saga is developed based on the ES6 generator.
Redux-Saga provides an API that helps achieve the following goals:
- Blocking events, blocking threads on the same line until some operations are completed
- Non-blocking events, making the code asynchronous
- Handle competition between multiple asynchronous requests
- Pause/throttling/de-bounce any action
How does Saga work?
Saga uses a combination of ES6 generator and async/await API to simplify asynchronous operations. It basically does its work on its separate threads where we can make multiple API calls. We can use their API to make each call synchronous or asynchronous, depending on the use case. The API provides the ability to make a thread wait on the same line until the request returns a response. Apart from that, this library provides many other APIs, which make API requests very easy to handle.
Consider our previous example: If we initialize a saga and configure it with Redux based on what is mentioned in its documentation, we can do the following:
userDetails: { … city: 'city', … };
So if we distribute it using a simple action of type FETCH_RESTAURANTS
, the Saga middleware will listen and respond. In fact, no action is used by middleware. It just listens and performs some other tasks, and distributes new actions if needed. By using this architecture, we can distribute multiple requests, each of which describes:
- When will the first request start?
- When will the first request be completed
- When will the second request start?
and so on.
In addition, you can see the advantages of fetchRestaurantSaga()
. We are currently using the call
API to implement blocking calls. Saga provides other APIs, such as fork()
, which implements non-blocking calls. We can combine blocking and non-blocking calls to maintain a structure that is suitable for our application.
With scalability, using saga is beneficial:
- We can build and group saga based on any specific task. We can trigger a saga from another saga by simply distributing an action.
- Since it is a middleware, the action we wrote will be a normal JS object, unlike thunk.
- Since we put the business logic inside saga (which is a middleware), it would be much easier to understand the React part of it if we know what saga is capable of.
- Errors can be easily monitored and distributed to storage through try/catch mode.
Using Redux-Observables
As stated in its documentation, "Epic is the core primitive of redux-observable" section:
- Epic is a function that receives the action stream and returns the action stream. That is, Epic runs in parallel with the normal Redux distribution channel, after the reducer has received them.
- action is always run through a reducer before epic receives them. Epic receives and outputs only another action stream. This is similar to Redux-Saga, because none of the actions are used by middleware. It just listens and performs some other tasks.
For our task, we can simply write the following code:
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
At first, this may seem a bit confusing. However, the more you understand RxJS, the easier it is to create an Epic.
Like saga, we can distribute multiple actions, each of which describes which part of the API request chain the thread is currently in.
In terms of scalability, we can split or combine Epic based on a specific task. Therefore, this library can help build scalable applications. If we understand the observable pattern of writing code, the code clarity is good.
My preferences
How to determine which library to use? It depends on how complex our API requests are.
How to choose between Redux-Saga and Redux-Observable? It depends on the learning generator or RxJS. Both are different concepts, but equally good enough. I suggest trying both to see which one is better for you.
Where is putting the business logic for processing APIs? It is best to put it before the reducer, but not in the component. The best way is in the middleware (using saga or observable).
You can read more React development articles at Codebrahma.
FAQs about asynchronous operations in React-Redux applications
What is the role of middleware in handling asynchronous operations in Redux?
Middleware in Redux plays a crucial role in handling asynchronous operations. It provides a third-party extension point between the distribution action and the action arrives at the reducer. Middleware can be used to record, modify, and even cancel actions, as well as to distribute other actions. In the context of asynchronous operations, middleware like Redux Thunk or Redux Saga allows you to write action creators that return functions instead of action . This function can then be used to delay the distribution of an action or to only distribute an action when a specific condition is met.
How does Redux Thunk help manage asynchronous operations?
Redux Thunk is a middleware that allows you to write action creators that return functions instead of action. thunk can be used to delay the distribution of actions or to distribute actions only when certain conditions are met. This feature makes it an excellent tool for handling asynchronous operations in Redux. For example, you can distribute an action to indicate the start of an API call, and then distribute another action when the call returns data or error message.
What is the difference between Redux Thunk and Redux Saga?
Redux Thunk and Redux Saga are both middleware for managing side effects in Redux, including asynchronous operations. The main difference between the two is their approach. Redux Thunk uses callback functions to handle asynchronous operations, while Redux Saga uses generator functions and more declarative methods. This makes Redux Saga more powerful and flexible, but also more complex. If your application has simple asynchronous operations, Redux Thunk may suffice. However, for more complex scenarios involving race conditions, cancellation, and if-else logic, Redux Saga may be a better choice.
How to handle errors in asynchronous operations in Redux?
Action can be distributed when an error occurs during an asynchronous operation to handle error handling in asynchronous operation in Redux. This action can take its error message as its payload. You can then handle this action in the reducer to update the status with the error message. This way, an error message can be displayed to the user or recorded for debugging.
How to test asynchronous action in Redux?
Asynchronous actions in Redux can be tested by mocking Redux storage and API calls. For Redux storage, you can use libraries like redux-mock-store
. For API calls, you can use libraries like fetch-mock
or nock
. In your tests, you can distribute an asynchronous action and then assert that the expected action has been distributed with the correct payload.
How to cancel asynchronous operations in Redux?
Middleware like Redux Saga can be used to cancel asynchronous operations in Redux. Redux Saga uses generator functions, which can be cancelled using cancel
effect. When cancel
effect is yielded, saga will be cancelled from its startup point until the current effect is cancelled.
How to handle race conditions in asynchronous operations in Redux?
Middleware like Redux Saga can be used to handle race conditions in asynchronous operations in Redux. Redux Saga provides effects like takeLatest
and takeEvery
that can be used to handle concurrent actions. For example, if the previously started saga task is still running when a new action is distributed, takeLatest
cancels the task.
How to use async/await with Redux Thunk?
Redux Thunk natively supports async/await. In your action creator, you can return asynchronous functions instead of regular functions. Inside this asynchronous function, you can use async/await to handle asynchronous operations. When the asynchronous operation is completed, the dispatch
function can be called using the action object.
How to handle loading state in asynchronous operations in Redux?
The loading state in asynchronous operations in Redux can be handled by distributing actions before and after asynchronous operations. The action distributed before the operation can set the load status to true, and the action distributed after the operation can set it to false. In your reducer, you can handle these actions to update the load state in the storage.
How to deal with side effects in Redux?
Side effects in Redux can be handled using middleware like Redux Thunk or Redux Saga. These middleware allows you to write action creators that return functions instead of action . This function can be used to perform side effects such as asynchronous operations, logging, and conditionally distributing actions.
The above is the detailed content of Async Operations in React Redux Applications. For more information, please follow other related articles on the PHP Chinese website!

The main difference between Python and JavaScript is the type system and application scenarios. 1. Python uses dynamic types, suitable for scientific computing and data analysis. 2. JavaScript adopts weak types and is widely used in front-end and full-stack development. The two have their own advantages in asynchronous programming and performance optimization, and should be decided according to project requirements when choosing.

Whether to choose Python or JavaScript depends on the project type: 1) Choose Python for data science and automation tasks; 2) Choose JavaScript for front-end and full-stack development. Python is favored for its powerful library in data processing and automation, while JavaScript is indispensable for its advantages in web interaction and full-stack development.

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

Choosing Python or JavaScript should be based on career development, learning curve and ecosystem: 1) Career development: Python is suitable for data science and back-end development, while JavaScript is suitable for front-end and full-stack development. 2) Learning curve: Python syntax is concise and suitable for beginners; JavaScript syntax is flexible. 3) Ecosystem: Python has rich scientific computing libraries, and JavaScript has a powerful front-end framework.

The power of the JavaScript framework lies in simplifying development, improving user experience and application performance. When choosing a framework, consider: 1. Project size and complexity, 2. Team experience, 3. Ecosystem and community support.

Introduction I know you may find it strange, what exactly does JavaScript, C and browser have to do? They seem to be unrelated, but in fact, they play a very important role in modern web development. Today we will discuss the close connection between these three. Through this article, you will learn how JavaScript runs in the browser, the role of C in the browser engine, and how they work together to drive rendering and interaction of web pages. We all know the relationship between JavaScript and browser. JavaScript is the core language of front-end development. It runs directly in the browser, making web pages vivid and interesting. Have you ever wondered why JavaScr


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Atom editor mac version download
The most popular open source editor

SublimeText3 Linux new version
SublimeText3 Linux latest version

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

SublimeText3 English version
Recommended: Win version, supports code prompts!
