fix: don't notify signals tracking AsyncSignal.future when transitioning to loading state#434
Conversation
|
|
||
| /// The future of the signal completer | ||
| Future<T> get completionFuture { | ||
| untracked(() { |
There was a problem hiding this comment.
This seems redundant as it was only accessed so it could be tracked.
There was a problem hiding this comment.
I think it is necessary, to avoid creating a dependency from the caller to the signal.value, which is what causes the original problem
There was a problem hiding this comment.
I am saying that it should be removed completely because value was not used or returned, it's original purpose was to register the caller as a dependent. Now that you're returning the internal signal, you can remove the untracked statement as it's no longer needed.
There was a problem hiding this comment.
Oh I see. The reason for this is that I somehow have to "trigger" the async computation. Without this call, the future won't ever be resolved. This is related to how StreamSignal is implemented, which is the base for FutureSignal.
signals.dart/packages/signals_core/lib/src/async/stream.dart
Lines 350 to 357 in 5ac7164
However, I just did it, to get it working quickly. There might be a more elegant way to do this. Will take a closer look at this 👍
There was a problem hiding this comment.
Oh, I didn't realize that .value had a side effect that needs to run to kick things off. This should probably be moved to the constructor but I admit that it is more efficient to not subscribe until the value is accessed.
There was a problem hiding this comment.
This is related to how StreamSignal is implemented, which is the base for FutureSignal
Would it be better if FutureSignal did not extend StreamSignal? I have been thinking about making that change for v7
There was a problem hiding this comment.
I generally like that there is a single implementation taking care of handling the async computation.
Given my current knowledge about the codebase I can't say whether separating FutureSignal from StreamSignal would make it easier or harder to implement changes/fixes such as this one.
If I had to make that decision, I'd look out for implementation details within StreamSignal that exist just to make FutureSignal work. If such code exists, then I'd say there is a good reason to no longer base FutureSignal on StreamSignal. However, if such code does not exist, then I think it makes more sense to keep it as it is.
There was a problem hiding this comment.
Moved the call into StreamSignal, assuming that it is not needed for all other async signals. This should make it more obvious why it exists for future readers.
|
Did a first implementation that passes all the existing tests. Here is what I'm planning to do before submitting the PR for review:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #434 +/- ##
==========================================
+ Coverage 64.42% 64.81% +0.38%
==========================================
Files 87 88 +1
Lines 2145 2194 +49
==========================================
+ Hits 1382 1422 +40
- Misses 763 772 +9
🚀 New features to boost your workflow:
|
…ing to loading state
f62c54b to
a067d1b
Compare
| bool _initialized = false; | ||
| bool get isCompleted => _initialized && _completer.isCompleted; |
There was a problem hiding this comment.
The _initialized flag is needed to make an existing test for simple AsyncSignals work, in case they are created in a completed state.
signals.dart/packages/signals_core/test/async/signal_test.dart
Lines 16 to 21 in 5ac7164
Didn't manage to figure out why an async signal is considered not completed after creating with data, so added this edge case in order to not break things.
| static Future<T> _ignoreUncaughtErrors<T>(Future<T> future) { | ||
| // Makes sure the future never reports an uncaught error to | ||
| // the current zone. Seems to be necessary to avoid uncaught | ||
| // errors to be reported when async signals are being used | ||
| // synchronously, i.e. when their .future is not used. | ||
| future.ignore(); | ||
| return future; | ||
| } |
There was a problem hiding this comment.
Here I'm really not sure if this is a good idea, but IMHO we have no other choice. Otherwise users might see a lot of uncaught errors even when they are handled synchronously through AsyncError
Fixes #433
AsyncSignal.futureto no longer notify when the async signal transitions into a loading state. The notification now happens when the async signal completes with data or error.AsyncSignal.completionfor tracking dependencies across async gaps without getting notified about state changes to loading states.