invalidatableFuture<T> method

(AsyncValue<T> Function(), void Function()) invalidatableFuture<T>(
  1. Future<T> futureFactory()
)

A side effect that allows you to watch a future lazily (by invoking the first callback) that can be invalidated lazily (by invoking the second callback).

You supply a futureFactory, which is a function that must return a new instance of a future to be watched.

See also refreshableFuture, which eagerly refreshes futures.

Note: this returns an AsyncValue<T> Function() because returning a function enables this side effect to determine whether or not there is any demand for the future itself, enabling it to be evaluated lazily.

Implementation

(AsyncValue<T> Function(), void Function()) invalidatableFuture<T>(
  Future<T> Function() futureFactory,
) {
  final runTxn = use.transactionRunner();
  final (getAsyncState, setAsyncState) =
      use.data<AsyncValue<T>>(AsyncLoading<T>(None<T>()));
  final (getFutureCancel, setFutureCancel) = use.data<void Function()?>(null);
  use.register((api) => api.registerDispose(() => getFutureCancel()?.call()));

  return (
    () {
      if (getFutureCancel() == null) {
        setAsyncState(AsyncLoading<T>(getAsyncState().data));
        final subscription = futureFactory().asStream().listen(
              (data) => setAsyncState(AsyncData(data)),
              onError: (Object error, StackTrace trace) => setAsyncState(
                AsyncError(error, trace, getAsyncState().data),
              ),
            );
        setFutureCancel(subscription.cancel);
      }

      return getAsyncState();
    },
    () {
      runTxn(() {
        getFutureCancel()?.call();
        setFutureCancel(null);
      });
    },
  );
}