DEV Community

loading...
Cover image for Asynchronous State Management with ActiveJS

Asynchronous State Management with ActiveJS

dabalyan profile image Ankit Singh Updated on ・4 min read

This article is a follow-up to a previous article about synchronous state management, State Management with a Single Line of Code. I'd suggest that you at least take a quick look at it so that you understand this one better. (It's okay if you don't feel like it, I'll try to keep it simple :)

I created a state-management library called ActiveJS, 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:

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')
Enter fullscreen mode Exit fullscreen mode

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.

import {AsyncSystem} from '@activejs/core';

// initialize an AsyncSystem, ready to receive, store, and share.
const asyncSystem = new AsyncSystem();
Enter fullscreen mode Exit fullscreen mode

AsyncSystem is a systematic combination of 4 separate reactive data structures that it creates internally, called 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

// using ES6 destructuring assignment
const {queryUnit, dataUnit, errorUnit, pendingUnit} = asyncSystem;
Enter fullscreen mode Exit fullscreen mode

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

// 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);
  }
}
Enter fullscreen mode Exit fullscreen mode

Setup API request trigger by subscribing to the queryUnit

// 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.
Enter fullscreen mode Exit fullscreen mode

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

// 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
Enter fullscreen mode Exit fullscreen mode

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

// dispatch a query
// it also sets the pendingUnit's value to true, automatically
queryUnit.dispatch(42)
Enter fullscreen mode Exit fullscreen mode

Retrying/Replaying the last API request

// 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
Enter fullscreen mode Exit fullscreen mode

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 built with AsyncSystem and RxJS operators, if you want to try it out yourself.

Here's the visual playground for 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
📖 ActiveJS Documentation
🤾‍♂️ ActiveJS Playground
💻 ActiveJS GitHub Repo (drop a ⭐ maybe :)

Discussion

pic
Editor guide
Collapse
doooreyn profile image
Reyn

As beautiful as art.

Collapse
dabalyan profile image
Ankit Singh Author

thanks Reyn :) this means a lot

Collapse
yamillanz profile image
Yamil Lanz

Excellent work!!...One question. An AsyncSystem can be configured with "persistent: true"? . Thanks

Collapse
dabalyan profile image
Ankit Singh Author

thanks, Yamil :)

yes, by providing {UNITS: {persistent: true}} in the contructor config you can make all 4 Units of an AysncSystem persistent. (see more here)