From de19084822b98ee37ad39feddd148789fe697f8e Mon Sep 17 00:00:00 2001 From: jinyus Date: Thu, 7 Dec 2023 15:07:46 -0500 Subject: [PATCH 1/2] let DerivedFutureBeacon extend FutureBeacon and use mixin for shared functionality with DerivedBeacon --- state_beacon/lib/src/beacons/derived.dart | 12 +-- .../lib/src/beacons/derived_future.dart | 55 ++-------- state_beacon/lib/src/beacons/future.dart | 102 ++++++++++-------- state_beacon/lib/src/state_beacon.dart | 4 +- 4 files changed, 69 insertions(+), 104 deletions(-) diff --git a/state_beacon/lib/src/beacons/derived.dart b/state_beacon/lib/src/beacons/derived.dart index 8f07f777..1d99d9b7 100644 --- a/state_beacon/lib/src/beacons/derived.dart +++ b/state_beacon/lib/src/beacons/derived.dart @@ -2,31 +2,25 @@ part of '../base_beacon.dart'; enum DerivedStatus { idle, running } -class DerivedBeaconBase extends ReadableBeacon { +mixin DerivedMixin on ReadableBeacon { late final VoidCallback _unsubscribe; void $setInternalEffectUnsubscriber(VoidCallback unsubscribe) { _unsubscribe = unsubscribe; } - DerivedBeaconBase(); - - void unsubscribe() { - _unsubscribe(); - } - void forceSetValue(T newValue) { _setValue(newValue, force: true); } @override void dispose() { - unsubscribe(); + _unsubscribe(); super.dispose(); } } -class DerivedBeacon extends DerivedBeaconBase { +class DerivedBeacon extends ReadableBeacon with DerivedMixin { DerivedBeacon({bool manualStart = false}) { _status = WritableBeacon( manualStart ? DerivedStatus.idle : DerivedStatus.running, diff --git a/state_beacon/lib/src/beacons/derived_future.dart b/state_beacon/lib/src/beacons/derived_future.dart index 1583594a..4f46ece3 100644 --- a/state_beacon/lib/src/beacons/derived_future.dart +++ b/state_beacon/lib/src/beacons/derived_future.dart @@ -6,61 +6,26 @@ enum DerivedFutureStatus { restarted, } -class DerivedFutureBeacon extends DerivedBeaconBase> { - DerivedFutureBeacon({bool manualStart = false, this.cancelRunning = true}) { +class DerivedFutureBeacon extends FutureBeacon + with DerivedMixin> { + DerivedFutureBeacon({bool manualStart = false, super.cancelRunning = true}) { if (manualStart) { - _status = WritableBeacon(DerivedFutureStatus.idle); - super.forceSetValue(AsyncIdle()); + _status.set(DerivedFutureStatus.idle); + _setValue(AsyncIdle()); } else { - _status = WritableBeacon(DerivedFutureStatus.running); + _status.set(DerivedFutureStatus.running); + _setValue(AsyncLoading()); } } - - final bool cancelRunning; - AsyncValue? _previousAsyncValue; - T? _lastData; - - @override - AsyncValue? get previousValue => _previousAsyncValue; - - /// The last data that was successfully loaded - /// This is useful when the current state is [AsyncError] or [AsyncLoading] - T? get lastData => _lastData; - - late final WritableBeacon _status; + final _status = WritableBeacon(); ReadableBeacon get status => _status; - var _executionID = 0; - - int startLoading() { - super.forceSetValue(AsyncLoading()); - return ++_executionID; - } - - void setAsyncValue(int exeID, AsyncValue value) { - // If the execution ID is not the same as the current one, - // then this is an old execution and we should ignore it - if (cancelRunning && exeID != _executionID) return; - - if (value is AsyncData) { - if (_lastData != null) { - // first time we get data, we don't have a previous value - - // ignore: null_check_on_nullable_type_parameter - _previousAsyncValue = AsyncData(_lastData!); - } - - _lastData = value.unwrapValue(); - } - - super.forceSetValue(value); - } - /// Starts executing an idle [Future] /// /// NB: Must only be called once /// /// Use [reset] to restart the [Future] + @override void start() { // can only start once if (_status.peek() != DerivedFutureStatus.idle) return; @@ -80,8 +45,6 @@ class DerivedFutureBeacon extends DerivedBeaconBase> { @override void dispose() { _status.dispose(); - _lastData = null; - _previousAsyncValue = null; super.dispose(); } } diff --git a/state_beacon/lib/src/beacons/future.dart b/state_beacon/lib/src/beacons/future.dart index 385a5d93..b420411c 100644 --- a/state_beacon/lib/src/beacons/future.dart +++ b/state_beacon/lib/src/beacons/future.dart @@ -3,27 +3,65 @@ part of '../base_beacon.dart'; class FutureBeacon extends ReadableBeacon> { var _executionID = 0; - FutureBeacon( - this._operation, { - bool manualStart = false, - this.cancelRunning = true, - }) : super(manualStart ? AsyncIdle() : AsyncLoading()) { - if (!manualStart) _init(); - } - final bool cancelRunning; - final Future Function() _operation; - AsyncValue? _previousAsyncValue; T? _lastData; - @override - AsyncValue? get previousValue => _previousAsyncValue; - /// The last data that was successfully loaded /// This is useful when the current state is [AsyncError] or [AsyncLoading] T? get lastData => _lastData; + @override + AsyncValue? get previousValue => _previousAsyncValue; + + FutureBeacon({this.cancelRunning = true, AsyncValue? initialValue}) + : super(initialValue); + + void start() {} + + int startLoading() { + _setValue(AsyncLoading()); + return ++_executionID; + } + + void setAsyncValue(int exeID, AsyncValue value) { + // If the execution ID is not the same as the current one, + // then this is an old execution and we should ignore it + if (cancelRunning && exeID != _executionID) return; + + if (value is AsyncData) { + if (_lastData != null) { + // first time we get data, we don't have a previous value + + // ignore: null_check_on_nullable_type_parameter + _previousAsyncValue = AsyncData(_lastData!); + } + + _lastData = value.unwrapValue(); + } + + _setValue(value, force: true); + } + + @override + void dispose() { + _lastData = null; + _previousAsyncValue = null; + super.dispose(); + } +} + +class DefaultFutureBeacon extends FutureBeacon { + DefaultFutureBeacon( + this._operation, { + bool manualStart = false, + super.cancelRunning = true, + }) : super(initialValue: manualStart ? AsyncIdle() : AsyncLoading()) { + if (!manualStart) _init(); + } + + final Future Function() _operation; + /// Resets the beacon by calling the [Future] again @override void reset() { @@ -36,6 +74,7 @@ class FutureBeacon extends ReadableBeacon> { /// NB: Must only be called once /// /// Use [reset] to restart the [Future] + @override void start() { // can only start once if (peek() is! AsyncIdle) return; @@ -43,44 +82,13 @@ class FutureBeacon extends ReadableBeacon> { } Future _init() async { - final currentTracker = ++_executionID; - if (peek() is! AsyncLoading) { - _setValue(AsyncLoading()); - } - - void updateOrIgnore(AsyncValue value) { - // if currentTacker != _tracker, another call to - // init has been made so we should ignore this result - if (cancelRunning && currentTracker != _executionID) { - return; - } - - if (value is AsyncData) { - if (_lastData != null) { - // first time we get data, we don't have a previous value - - // ignore: null_check_on_nullable_type_parameter - _previousAsyncValue = AsyncData(_lastData!); - } - - _lastData = value.unwrapValue(); - } - - _setValue(value); - } + final currentExeID = startLoading(); try { final result = await _operation(); - return updateOrIgnore(AsyncData(result)); + return setAsyncValue(currentExeID, AsyncData(result)); } catch (e, s) { - return updateOrIgnore(AsyncError(e, s)); + return setAsyncValue(currentExeID, AsyncError(e, s)); } } - - @override - void dispose() { - _lastData = null; - _previousAsyncValue = null; - super.dispose(); - } } diff --git a/state_beacon/lib/src/state_beacon.dart b/state_beacon/lib/src/state_beacon.dart index 7124942e..3a3b81f4 100644 --- a/state_beacon/lib/src/state_beacon.dart +++ b/state_beacon/lib/src/state_beacon.dart @@ -282,7 +282,7 @@ abstract class Beacon { bool manualStart = false, bool cancelRunning = true, }) { - return FutureBeacon( + return DefaultFutureBeacon( future, manualStart: manualStart, cancelRunning: cancelRunning, @@ -356,7 +356,7 @@ abstract class Beacon { /// } /// } /// ``` - static DerivedFutureBeacon derivedFuture( + static FutureBeacon derivedFuture( Future Function() compute, { bool manualStart = false, bool cancelRunning = true, From 088e3fb1a9b8accee82c0501aed461d5c6f46121 Mon Sep 17 00:00:00 2001 From: jinyus Date: Thu, 7 Dec 2023 15:08:05 -0500 Subject: [PATCH 2/2] remove unneeded test --- state_beacon/test/async_beacon_test.dart | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/state_beacon/test/async_beacon_test.dart b/state_beacon/test/async_beacon_test.dart index d75337c9..bdc21a72 100644 --- a/state_beacon/test/async_beacon_test.dart +++ b/state_beacon/test/async_beacon_test.dart @@ -100,29 +100,6 @@ void main() { expect(ran, equals(2)); }); - test('should stop listening after unsubscribing', () async { - var count = Beacon.writable(0); - - var ran = 0; - - var futureBeacon = Beacon.derivedFuture(() async { - count.value; - return ++ran; - }); - - await Future.delayed(Duration(milliseconds: 100)); - - expect(ran, equals(1)); - - futureBeacon.unsubscribe(); - - count.value = 1; // Changing dependency - - await Future.delayed(Duration(milliseconds: 100)); - - expect(ran, equals(1)); - }); - test('should not execute until start() is called', () async { var count = Beacon.writable(0);