Asynchronous State Management with ActiveJS
Tags: javascript, webdev, typescript, productivity
Published: 2020-11-03 22:12:53

> This article is a follow-up to a previous article about synchronous state management, [State Management with a Single Line of Code](https://dev.to/dabalyan/state-management-with-a-single-line-of-code-2llg). I'd suggest that you at least take a quick look at it so that you understand this one better. <small>(It's okay if you don't feel like it, I'll try to keep it simple :)</small>

I created a state-management library called [ActiveJS](https://activejs.dev), where **async-state-management** isn't an afterthought. ActiveJS strives to make state-management simple, and get rid of walls of code required for the current mainstream solutions.

So, without any further adieu, let's get started.

This is what we're going to target and achieve with as little code as possible.

#### The 4 major aspects of an Async API call:
1. Making a **Query**
2. Handling the **Response**
3. Handling the **Error**
4. Checking or listening to the **Pending-Status**

#### And then some situational aspects, like:
1. Storing the received response
2. Sharing the response and status of the API call
3. Being able to retry or replay the request
4. Clearing the stored error on a successful response, etc.

Now that we know what we need to achieve, all we need is some kind of system that can do all this, where we don't have to set-up all the mechanisms from scratch.

#### This is all the code we're going to need to achieve all of the above:
```ts
import {AsyncSystem} from '@activejs/core'

const asyncSystem = new AsyncSystem()
const {queryUnit, dataUnit, errorUnit, pendingUnit} = asyncSystem

async function fetchAndShare(query) {
  try {
    const response = await fetch('https://xyz.com/?q=' + query)
    const data = await response.json()
    dataUnit.dispatch(data)
  } catch (err) {
    errorUnit.dispatch(err)
  }
}

queryUnit.future$.subscribe(query => fetchAndShare(query))
queryUnit.dispatch('some query')
```

If you don't understand what is going on, that's okay, we'll understand it together, line by line.

#### The most important part is the [AsyncSystem](https://docs.activejs.dev/fundamentals/systems/asyncsystem).
```ts
import {AsyncSystem} from '@activejs/core';

// initialize an AsyncSystem, ready to receive, store, and share.
const asyncSystem = new AsyncSystem();
```

AsyncSystem is a systematic combination of 4 separate reactive data structures that it creates internally, called [Units](https://docs.activejs.dev/fundamentals/units), these Units pertain to each major aspect of an async API call namely **Query**, **Data**, **Error**, and **Pending-Status**.

AsyncSystem also creates some custom relationships among these Units to achieve some of the situational aspects that we mentioned above, these relationships can be enabled or disabled by passing configuration flags to the AsysnSystem.

#### Extract the data structures for easier access
```ts
// using ES6 destructuring assignment
const {queryUnit, dataUnit, errorUnit, pendingUnit} = asyncSystem;
```
`queryUnit` to store, and share the **Query**, and to trigger the API call
`dataUnit` to store, and share the **Response** data
`errorUnit` to store, and share the **Error** data
`pendingUnit` to store, and share the **Pending-Status**
 
#### Setup the data fetching logic using the native `fetch` API
```ts
// a function to fetch data and disptch the response appropriately
async function fetchAndShare(query) {
  try {
    // fetch data using fetch API
    const response = await fetch('https://xyz.com/?q=' + query);
    // extract the JSON data
    const data = await response.json();

    // dispatch data to the dataUnit
    // it also sets the pendingUnit's value to false, automatically
    // and, it sets the errorUnit's value to undefined, automatically
    dataUnit.dispatch(data);
  } catch (err) {
    // dispatch error to errorUnit
    // it also sets the pendingUnit's value to false, automatically
    errorUnit.dispatch(err);
  }
}
```

#### Setup API request trigger by subscribing to the `queryUnit`
```ts
// whenever a value is dispatched to queryUnit,
// the 'fetchAndShare' will get called
queryUnit.future$.subscribe(query => fetchAndShare(query));
// we can also subscribe to the queryUnit directly, but by using
// future$ we make sure that we start making API calls only after a 
// new dispach, otherwise it'd have already made a call.
```

We can already start listening for the values by subscribing to the reactive data structures we just extracted above.

#### Listen for values, from anywhere and as many places as needed
```ts
// listen for queries
queryUnit.subscribe(query => console.log(query));
// logs undefined immediately and will log future values

// listen for data
dataUnit.subscribe(data => console.log(data));
// logs undefined immediately and will log future values

// listen for errors
errorUnit.subscribe(error => console.log(error));
// logs undefined immediately and will log future values

// listen for pending status
pendingUnit.subscribe(isPending => console.log(isPending));
// logs false immediately and will log future values
```

All that is left is triggering the API call, which can also be done from anywhere by dispatching a value to the `queryUnit`, the rest will be handled by the AsyncSystem and the logic we just wrote.

#### Trigger an API request
```ts
// dispatch a query
// it also sets the pendingUnit's value to true, automatically
queryUnit.dispatch(42)
```

#### Retrying/Replaying the last API request
```ts
// replay the query
// it also sets the pendingUnit's value to true, automatically
queryUnit.replay()
// it'll re-emit the current query value (i.e. 42 in this case),
// and the rest will work the same as triggering a new API request
```

That's it, folks, all done.

There are even more things that ActiveJS can do for you very efficiently, but maybe let's discuss that in another article.

Here's a simple [StackBlitz Typeahead example](https://stackblitz.com/edit/typescript-r6zjrg?file=index.ts) built with AsyncSystem and RxJS operators, if you want to try it out yourself.

Here's the [visual playground for AsyncSystem](https://activejs.dev/#/playground/asyncsystem), which you can try out without writing any code.

---

If you reached here,
Please let me know if I added too much information or too little.
Also, let me know what would you like to see ActiveJS do in the next article.

Cheers

[🌏 ActiveJS Website](https://activejs.dev)
[📖 ActiveJS Documentation](https://docs.activejs.dev)
[🤾‍♂️ ActiveJS Playground](https://activejs.dev/#/playground/asyncsystem)
[💻 ActiveJS GitHub Repo](https://github.com/activejs/activejs) (drop a ⭐ maybe :)