Taming network with redux-requests, part 3 - Race conditions and requests aborts
Tags: javascript, redux, react, webdev
Published: 2020-07-21 23:31:51

In the [previous part of this series](https://dev.to/klis87/taming-network-with-redux-requests-part-2-basic-usage-o2o) we learned the basics of [redux-requests](https://redux-requests.klisiczynski.com). 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:
```js
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:
```js
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:
```js
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`
1. `FETCH_BOOKS`
1. `FETCH_BOOKS_ABORT`
1. `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:
```js
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:
```js
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:
```js
import { abortRequests } from '@redux-requests/core';

// abort everything
dispatch(abortRequests());

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

## What's next?

In [the next part](https://dev.to/klis87/taming-network-with-redux-requests-part-4-automatic-normalisation-1l8j) we will discuss data normalisation concept and how we could automatize this process.