Skip to content

FutureSignal awaiting an AsyncSignal.future is executed too often #433

@Yegair

Description

@Yegair

Description

I have a FutureSignal that is computed by awaiting the value of another FutureSignal.
The computed signal is executed twice, although the source signal only ever emits a single value.

Reproduction

test('computed future emits once', () async {
  int calls = 0;
  int signalCalls = 0;
  int computedCalls = 0;
  int heavyComputationCalls = 0;

  Future<int> future() async {
    calls++;
    await Future.delayed(const Duration(milliseconds: 5));
    return 10;
  }

  final signal = futureSignal(() async {
    signalCalls++;
    return await future();
  });

  Future<int> heavyComputation(int value) async {
    heavyComputationCalls++;
    await Future.delayed(const Duration(milliseconds: 5));
    return value * 2;
  }

  final computed = futureSignal(
    () async {
      computedCalls++;
      final value = await signal.future;
      return await heavyComputation(value);
    },
    dependencies: [signal],
  );

  final result = await computed.future;

  expect(result, 20, reason: 'unexpected result');
  expect(calls, 1, reason: 'the future function was called an unexpected number of times');
  expect(signalCalls, 1, reason: 'the signal function was called an unexpected number of times');
  expect(heavyComputationCalls, 1, reason: 'the heavy computation function was called an unexpected number of times');
  expect(computedCalls, 1, reason: 'the computed function was called an unexpected number of times');
});

This test will fail with

Expected: <1>
  Actual: <2>
the heavy computation function was called an unexpected number of times

I assume the reason is that future signals have a value of AsyncState that does change twice (AsyncLoading -> AsyncData).

However, I am wondering if there might be a way to improve the AsyncSignal.future in a way that causes the dependent signals to be computed only once the AsyncData (or AsyncError) is emitted. This would be especially helpful in cases where the computed future signal does some heavy lifting (like running something on a separate isolate).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions