Skip to content

Commit

Permalink
Merge pull request #5 from jinyus/dev
Browse files Browse the repository at this point in the history
Expose DerivedFutureBeacon as FutureBeacon
  • Loading branch information
jinyus authored Dec 7, 2023
2 parents 5b7afdf + 088e3fb commit 3ea936d
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 127 deletions.
12 changes: 3 additions & 9 deletions state_beacon/lib/src/beacons/derived.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,25 @@ part of '../base_beacon.dart';

enum DerivedStatus { idle, running }

class DerivedBeaconBase<T> extends ReadableBeacon<T> {
mixin DerivedMixin<T> on ReadableBeacon<T> {
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<T> extends DerivedBeaconBase<T> {
class DerivedBeacon<T> extends ReadableBeacon<T> with DerivedMixin<T> {
DerivedBeacon({bool manualStart = false}) {
_status = WritableBeacon(
manualStart ? DerivedStatus.idle : DerivedStatus.running,
Expand Down
55 changes: 9 additions & 46 deletions state_beacon/lib/src/beacons/derived_future.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,26 @@ enum DerivedFutureStatus {
restarted,
}

class DerivedFutureBeacon<T> extends DerivedBeaconBase<AsyncValue<T>> {
DerivedFutureBeacon({bool manualStart = false, this.cancelRunning = true}) {
class DerivedFutureBeacon<T> extends FutureBeacon<T>
with DerivedMixin<AsyncValue<T>> {
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<T>? _previousAsyncValue;
T? _lastData;

@override
AsyncValue<T>? 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<DerivedFutureStatus> _status;
final _status = WritableBeacon<DerivedFutureStatus>();
ReadableBeacon<DerivedFutureStatus> get status => _status;

var _executionID = 0;

int startLoading() {
super.forceSetValue(AsyncLoading());
return ++_executionID;
}

void setAsyncValue(int exeID, AsyncValue<T> 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;
Expand All @@ -80,8 +45,6 @@ class DerivedFutureBeacon<T> extends DerivedBeaconBase<AsyncValue<T>> {
@override
void dispose() {
_status.dispose();
_lastData = null;
_previousAsyncValue = null;
super.dispose();
}
}
102 changes: 55 additions & 47 deletions state_beacon/lib/src/beacons/future.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,65 @@ part of '../base_beacon.dart';
class FutureBeacon<T> extends ReadableBeacon<AsyncValue<T>> {
var _executionID = 0;

FutureBeacon(
this._operation, {
bool manualStart = false,
this.cancelRunning = true,
}) : super(manualStart ? AsyncIdle() : AsyncLoading()) {
if (!manualStart) _init();
}

final bool cancelRunning;
final Future<T> Function() _operation;

AsyncValue<T>? _previousAsyncValue;
T? _lastData;

@override
AsyncValue<T>? 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<T>? get previousValue => _previousAsyncValue;

FutureBeacon({this.cancelRunning = true, AsyncValue<T>? initialValue})
: super(initialValue);

void start() {}

int startLoading() {
_setValue(AsyncLoading());
return ++_executionID;
}

void setAsyncValue(int exeID, AsyncValue<T> 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<T> extends FutureBeacon<T> {
DefaultFutureBeacon(
this._operation, {
bool manualStart = false,
super.cancelRunning = true,
}) : super(initialValue: manualStart ? AsyncIdle() : AsyncLoading()) {
if (!manualStart) _init();
}

final Future<T> Function() _operation;

/// Resets the beacon by calling the [Future] again
@override
void reset() {
Expand All @@ -36,51 +74,21 @@ class FutureBeacon<T> extends ReadableBeacon<AsyncValue<T>> {
/// NB: Must only be called once
///
/// Use [reset] to restart the [Future]
@override
void start() {
// can only start once
if (peek() is! AsyncIdle) return;
_init();
}

Future<void> _init() async {
final currentTracker = ++_executionID;
if (peek() is! AsyncLoading) {
_setValue(AsyncLoading());
}

void updateOrIgnore(AsyncValue<T> 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();
}
}
4 changes: 2 additions & 2 deletions state_beacon/lib/src/state_beacon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ abstract class Beacon {
bool manualStart = false,
bool cancelRunning = true,
}) {
return FutureBeacon<T>(
return DefaultFutureBeacon<T>(
future,
manualStart: manualStart,
cancelRunning: cancelRunning,
Expand Down Expand Up @@ -356,7 +356,7 @@ abstract class Beacon {
/// }
/// }
/// ```
static DerivedFutureBeacon<T> derivedFuture<T>(
static FutureBeacon<T> derivedFuture<T>(
Future<T> Function() compute, {
bool manualStart = false,
bool cancelRunning = true,
Expand Down
23 changes: 0 additions & 23 deletions state_beacon/test/async_beacon_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 3ea936d

Please sign in to comment.