Logo

Async Capsules

Often, you want to make a capsule that wraps around some asynchronous code so that asynchronous code can be utilized in other capsules. This poses an interesting problem, as every capsule is synchronous by nature. The solution: side effects! Side effects are indeed the mechanism that allows for external events to trigger capsule updates, which is exactly what we need here.

An Example#

Creating Asynchronous Capsules

Refreshable Asynchronous Capsules#

This section is Dart/Flutter only, as futures are handled slightly differently in Rust.

The created delayedCapsule in the above example is just a read-only "view" of the future. It doesn't contain any state of its own and merely stores the current value of the async capsule. If you want to refresh the delayed capule's state, say by fetching new data from online, we need a slightly different approach which will swap in a new future. refreshableFuture side effect to the rescue!

(AsyncValue<int>, void Function()) refreshableDelayedCapsule(CapsuleHandle use) {
  return use.refreshableFuture(() {
    return Future.delayed(const Duration(seconds: 1), () => 1234);
  });
}

There is also an equivalent for just invalidation: invalidatableFuture. The difference is that invalidatableFuture only executes a new future when it is currently in use, whereas refreshableFuture will always execute a new future when refreshed.

What is AsyncValue/AsyncState?#

You may notice that when applying the asynchronous side effects, you are given data of type AsyncValue<T>/AsyncState<T>.

This is entirely intentional; it allows you to handle the current state of asynchronous code synchronously, which is needed as ReArch builds are completely synchronous and (in Dart) cannot throw. As such, it is highly recommended that you learn how to properly work with AsyncValue/AsyncState instead of trying to avoid it.

Dart's AsyncValue#

AsyncValues represent one of three states for futures and streams:

  • Future/stream is still loading and has not emitted anything (AsyncLoading)
  • Future/stream emitted data (AsyncData)
  • Future/stream emitted an error (AsyncError)

See AsyncValue's API docs for more.

Rust's AsyncState#

AsyncStates represent one of two states for futures:

  • Future is still loading and has not emitted anything (Loading)
  • Future emitted data (Complete)

See AsyncState's API docs for more.