DEV Community

Konrad Lisiczyński
Konrad Lisiczyński

Posted on

Taming network with redux-requests, part 3 - Race conditions and requests aborts

In the previous part of this series we learned the basics of redux-requests. With that knowledge we can start discussing real apps problems and how we can solve them. Let's start with race conditions!

Race conditions

Now, imagine we need to fetch books which are paginated on the server. We start with writing a request action:

const fetchBooks = page => ({
  type: 'FETCH_BOOKS',
  request: {
    url: '/books',
    params: { page },
  },
});

We pass page param to fetchBooks action, because as described earlier, books are paginated on the backend side.

Once we have the action ready, let's download the 1st page of books:

store.dispatch(fetchBooks(1));

What will happen then? FETCH_BOOKS action will be dispatched, then AJAX request will be made and depending on the outcome, either FETCH_BOOKS_SUCCESS or FETCH_BOOKS_ERROR action will be dispatched with the server response.

But there is yet another possibility, imagine that we dispatch fetchBooks whenever a user wants to see a page. There is an interesting case, when the user is faster than our network. Let's simulate this behaviour:

store.dispatch(fetchBooks(1));
store.dispatch(fetchBooks(2));

In above situation, we ask for page 2 while request for page 1 is still pending. That's it! We have a potential race condition here! It could easily happen that response for page 2 will arrive earlier than for 1, especially on slower network! We can prevent that with requests aborts!

Requests aborts

Now, coming back to above example, what will actually happen is the following dispatch sequence:

  1. FETCH_BOOKS
  2. FETCH_BOOKS
  3. FETCH_BOOKS_ABORT
  4. FETCH_BOOKS_SUCCESS

What is FETCH_BOOKS_ABORT? As you probably know, AJAX requests can be possibly aborted. Sometimes you might want to cancel a pending request because its response is not needed anymore. Aborting is a good thing then because it releases resources. But often aborting requests is even more important because it prevents many race conditions bugs before they even happen!

Importance of requests aborts

In real life, you cannot predict how long a request will take. If you fetch 1st page of books, then quickly 2nd one, it could easily happen that response for the 1st page could be received after 2nd, despite the fact request order was different! So without being cautious here, user could see books from page 1 being on 2nd page!

So, going back, redux-requests has first class support for requests aborts. By default, if a query of a given type is pending and a new one is fired, the previous request will be automatically aborted.

Requests aborts configuration

By default only queries are aborted this way, mutations are not. You can easily change those defaults by a special takeLatest option, which can be passed either to handleRequests for global configuration or in request action meta.

If for a some reason you would like to prevent aborts for FETCH_BOOKS, you could do it like that:

const fetchBooks = page => ({
  type: FETCH_BOOKS,
  request: {
    url: '/books',
    params: { page },
  },
  meta: {
    takeLatest: false,
  },
});

Or... if you had a mutation which you would like to have aborted, you would add meta.takeLatest: true.

As mentioned above, you can configure it globally by using takeLatest option in handleRequest. Default implementation uses aborts only for queries and it looks like that:

import { isRequestActionQuery } from '@redux-requests/core';

const takeLatest = action => isRequestActionQuery(action);
// or just shorter
// const takeLatest = isRequestActionQuery;

abortRequests

Sometimes you might need to abort some pending requests manually. You can use abortRequests action to do it, for example:

import { abortRequests } from '@redux-requests/core';

// abort everything
dispatch(abortRequests());

// abort FETCH_BOOKS
dispatch(abortRequests([FETCH_BOOKS]));

What's next?

In the next part we will discuss data normalisation concept and how we could automatize this process.

Top comments (0)