Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEA-2771 Add ActionV2 w/o null safety #188

Merged
merged 7 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions lib/src/action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,22 @@ import 'package:w_common/disposable.dart';

import 'package:w_flux/src/constants.dart' show v3Deprecation;

/// Like [ActionV2], but payloads cannot be made non-nullable since the argument
/// to [call] is optional.
@Deprecated('Use ActionV2 instead, which supports non-nullable payloads.')
class Action<T> extends ActionV2<T> {
sorenthompson-wk marked this conversation as resolved.
Show resolved Hide resolved
@override
String get disposableTypeName => 'Action';

@override
Future call([T payload]) => super.call(payload);
}

/// A command that can be dispatched and listened to.
///
/// An [Action] manages a collection of listeners and the manner of
/// An [ActionV2] manages a collection of listeners and the manner of
/// their invocation. It *does not* rely on [Stream] for managing listeners. By
/// managing its own listeners, an [Action] can track a [Future] that completes
/// managing its own listeners, an [ActionV2] can track a [Future] that completes
/// when all registered listeners have completed. This allows consumers to use
/// `await` to wait for an action to finish processing.
///
Expand All @@ -45,15 +56,15 @@ import 'package:w_flux/src/constants.dart' show v3Deprecation;
/// when a consumer needs to check state changes immediately after invoking an
/// action.
///
class Action<T> extends Object with Disposable implements Function {
class ActionV2<T> extends Object with Disposable implements Function {
sorenthompson-wk marked this conversation as resolved.
Show resolved Hide resolved
@override
String get disposableTypeName => 'Action';
String get disposableTypeName => 'ActionV2';

List _listeners = [];
List<_ActionListener<T>> _listeners = [];

/// Dispatch this [Action] to all listeners. If a payload is supplied, it will
/// be passed to each listener's callback, otherwise null will be passed.
Future call([T payload]) {
/// Dispatch this [ActionV2] to all listeners. The payload will be passed to
/// each listener's callback.
Future call(T payload) {
// Invoke all listeners in a microtask to enable waiting on futures. The
// microtask queue is emptied before the event loop continues. This ensures
// synchronous listeners are invoked in the current tick of the event loop
Expand All @@ -65,23 +76,24 @@ class Action<T> extends Object with Disposable implements Function {
// a [Stream]-based action implementation. At smaller sample sizes this
// implementation slows down in comparison, yielding average times of 0.1 ms
// for stream-based actions vs. 0.14 ms for this action implementation.
Future callListenerInMicrotask(l) => Future.microtask(() => l(payload));
Future callListenerInMicrotask(_ActionListener<T> l) =>
Future.microtask(() => l(payload));
return Future.wait(_listeners.map(callListenerInMicrotask));
}

/// Cancel all subscriptions that exist on this [Action] as a result of
/// Cancel all subscriptions that exist on this [ActionV2] as a result of
/// [listen] being called. Useful when tearing down a flux cycle in some
/// module or unit test.
@Deprecated('Use (and await) dispose() instead. $v3Deprecation')
void clearListeners() {
_listeners.clear();
}

/// Supply a callback that will be called any time this [Action] is
/// Supply a callback that will be called any time this [ActionV2] is
/// dispatched. A payload of type [T] will be passed to the callback if
/// supplied at dispatch time, otherwise null will be passed. Returns an
/// [ActionSubscription] which provides means to cancel the subscription.
ActionSubscription listen(dynamic onData(T event)) {
ActionSubscription listen(dynamic Function(T event) onData) {
_listeners.add(onData);
return ActionSubscription(() => _listeners.remove(onData));
}
Expand All @@ -97,13 +109,15 @@ class Action<T> extends Object with Disposable implements Function {
}
}

/// A subscription used to cancel registered listeners to an [Action].
typedef _ActionListener<T> = dynamic Function(T event);

/// A subscription used to cancel registered listeners to an [ActionV2].
class ActionSubscription {
Function _onCancel;

ActionSubscription(this._onCancel);

/// Cancel this subscription to an [Action]
/// Cancel this subscription to an [ActionV2]
void cancel() {
if (_onCancel != null) {
_onCancel();
Expand Down
Loading
Loading