4. async-states: The producer

Mohamed EL AYADI
4 min readFeb 20, 2023

--

Promises, async/await, generators, reducers… or even nothing! These are all supported out of the box by the library.

This post is a part of the following blog post series:

  1. async-states: the state management library
  2. async-states: Using react
  3. async-states: The source object
  4. async-states: The producer
  5. async-states: Advanced concepts
  6. async-states: vs react-query vs redux vs recoil
  7. react-async-states: SSR

Plan

  • The producer types
  • The philosophy
  • Conclusion

The producer types

The producer is the function that does the work and produces the state value. It can be of the following types:

Function returning a Promise/thenable

If the producer function returns a Promise, the state is captured when it resolves or throws.

Here are some examples:

Promises producers

Async/await function

async/await basically returns a Promise when invoked, so it just naturally works the same as promises.

Generator

Generators are supported and can be either synchronous or asynchronous (if you yield a promise, you go async). Generators will stop the work immediately and not yield when the run gets aborted.

generator producer

Synchronous function

It can be a function that returns a value immediately other than a Promise and a Generator, in this case, there is no pending state and status will pass directly from initial|success|error to success|error.

reducers or sync functions

Notice the createReducerProducer that let you write in a reducer style or migrate incrementally an existing reducer pattern.

Nothing

Well, you can use no producer at all:

Note: run in this case delegates its work to setState.

The philosophy

While manipulating a state in general, there are three common ways to set the value of your data:

  • You set it imperatively in there, for this, there is setState
  • You perform some work that gives you this value ( sync|async|generator) and you get the result, for this there is producer and the run functions
  • State is emitted from a remote place such as intervals, websockets or whatever. For this, until I figure out the RemoteAsyncState abstraction, there is emit that can mimic this.

To support this, the idea is to create a function that accepts one single propswith enough power to cover all needs while having the ability to perform good user and developer experiences.

The producer’s power

Like a keep repeating, the producer’s power comes mainly from its props; Here are all the properties:

  • args: any[]retrieved as arguments of run or runp ; or autoRunArgs from useAsyncState. So when you perform a .run(arg1, arg2, arg3) you would receive these values as an array called args.
  • payload: Record<string, any> | undefined A shallow copy the payload present in the state instance at the moment of the run. You can set it manually via mergePayload function; or while passing it to useAsyncState (pay attention to deps!)
  • onAbort((reason?) => void): void Let you register callbacks that will be invoked when the abort occurs. Should cleanup the work actually ongoing by the producer; for example aborting a fetch request, clearing a timeout/interval, disconnecting from a websocket, terminating a worker… and so on
  • run(input, config, ...args): AbortFn Read about it here in the docs. It allows running any state and getting the abort function. (It can be chained like props.onAbort(props.run('state_key')) which will lead to cascading cancellations.
  • runp(input, config, ...args)Read about it here in the docs. Similar to run, but returns a Promise to the resolve of the state so that you can chain using .then or await directly.
  • selectRead about it here in the docs. Selects the current state of any state
  • emit Read about it here in the docs: Emits updates to the state after the producer resolve; this allows changing the state from remote events like intervals or websockets clients!
  • lastSuccess The last registered succeeded value; useful for reducers or infinite lists
  • getState Since producer may stay alive after return, getState may be useful to get the current state at any point of time.
  • isAborted(): boolean Returns whether the current run was aborted or not
  • abort(reason?: any): void Aborts the current run, if it occurs before going pending, no state update will be performed and everything will be completely bailed out!

Note: If you throw inside the producer, the state will pass to error with whatever thrown as data.

Conclusion

The producer will cover your needs from having an imperative state without any “producer”, to all producer types to perform data fetching, external events atomic changes, events emission, proper cleanup registration and many more. All the way while allowing you to tune and customize your experience as seen from the previous posts.

Next, to push the bar even higher, you will learn about some advanced concepts that the library offers. Yes! all of what we’ve seen until now are basic state manipulation. We haven’t talked yet about forks, lanes or pools and clusters! Next blog please!

--

--