@@ -62,102 +62,63 @@ class UpdateNotification {
62
62
static StreamTransformer <UpdateNotification , UpdateNotification >
63
63
filterTablesTransformer (Iterable <String > tables) {
64
64
Set <String > normalized = {for (var table in tables) table.toLowerCase ()};
65
-
66
- return StreamTransformer .fromBind (
67
- (source) => source.where ((data) => data.containsAny (normalized)));
65
+ return StreamTransformer <UpdateNotification ,
66
+ UpdateNotification >.fromHandlers (handleData: (data, sink) {
67
+ if (data.containsAny (normalized)) {
68
+ sink.add (data);
69
+ }
70
+ });
68
71
}
69
72
}
70
73
71
- /// Given an [input] stream, returns a stream that will throttle events for each
72
- /// listener .
74
+ /// Given a broadcast stream, return a singular throttled stream that is throttled.
75
+ /// This immediately starts listening .
73
76
///
74
77
/// Behaviour:
75
78
/// If there was no event in "timeout", and one comes in, it is pushed immediately.
76
79
/// Otherwise, we wait until the timeout is over.
77
80
Stream <T > _throttleStream <T extends Object >(Stream <T > input, Duration timeout,
78
- {bool throttleFirst = false , T Function (T , T )? add, T ? addOne}) {
79
- return Stream .multi (
80
- (downstream) {
81
- Timer ? activeDelay;
82
- T ? pendingData;
83
-
84
- bool needsToDelay () {
85
- return downstream.isPaused || activeDelay != null ;
86
- }
87
-
88
- void scheduleDelay () {
89
- assert (activeDelay == null );
90
-
91
- if (! needsToDelay ()) {
92
- activeDelay = Timer (timeout, () {
93
- activeDelay = null ;
94
-
95
- if (! needsToDelay ()) {
96
- if (pendingData case final pending? ) {
97
- pendingData = null ;
98
- downstream.addSync (pending);
99
- scheduleDelay ();
100
- }
101
- }
102
- });
103
- }
104
- }
105
-
106
- void cancelTimer () {
107
- activeDelay? .cancel ();
108
- activeDelay = null ;
109
- }
110
-
111
- var listener = input.listen (
112
- (data) {
113
- if (needsToDelay ()) {
114
- // We can't send this yet, so combine / replace the value to send when
115
- // the wait is over.
116
- if (pendingData != null && add != null ) {
117
- pendingData = add (pendingData as T , data);
118
- } else {
119
- pendingData = data;
120
- }
121
- } else {
122
- // We can forward this event directly, but need to wait for a timeout
123
- // before sending the next event.
124
- assert (pendingData == null );
125
- downstream.addSync (data);
126
- scheduleDelay ();
127
- }
128
- },
129
- onError: downstream.addErrorSync,
130
- onDone: () {
131
- cancelTimer ();
132
- if (pendingData case final pending? ) {
133
- downstream.addSync (pending);
134
- }
135
- downstream.closeSync ();
136
- },
137
- );
138
-
139
- if (addOne != null ) {
140
- downstream.add (addOne);
141
- }
142
- if (throttleFirst) {
143
- scheduleDelay ();
144
- }
81
+ {bool throttleFirst = false , T Function (T , T )? add, T ? addOne}) async * {
82
+ var nextPing = Completer <void >();
83
+ var done = false ;
84
+ T ? lastData;
85
+
86
+ var listener = input.listen ((data) {
87
+ if (lastData != null && add != null ) {
88
+ lastData = add (lastData! , data);
89
+ } else {
90
+ lastData = data;
91
+ }
92
+ if (! nextPing.isCompleted) {
93
+ nextPing.complete ();
94
+ }
95
+ }, onDone: () => done = true );
145
96
146
- downstream.onResume = () {
147
- if (! needsToDelay ()) {
148
- if (pendingData case final pending? ) {
149
- pendingData = null ;
150
- downstream.add (pending);
151
- scheduleDelay ();
152
- }
153
- }
154
- };
97
+ try {
98
+ if (addOne != null ) {
99
+ yield addOne;
100
+ }
101
+ if (throttleFirst) {
102
+ await Future .delayed (timeout);
103
+ }
104
+ while (! done) {
105
+ // If a value is available now, we'll use it immediately.
106
+ // If not, this waits for it.
107
+ await nextPing.future;
108
+ // Capture any new values coming in while we wait.
109
+ nextPing = Completer <void >();
110
+ T data = lastData as T ;
111
+ // Clear before we yield, so that we capture new changes while yielding
112
+ lastData = null ;
113
+ yield data;
114
+ // Wait a minimum of this duration between tasks
115
+ await Future .delayed (timeout);
116
+ }
117
+ } finally {
118
+ if (lastData case final data? ) {
119
+ yield data;
120
+ }
155
121
156
- downstream.onCancel = () {
157
- cancelTimer ();
158
- return listener.cancel ();
159
- };
160
- },
161
- isBroadcast: input.isBroadcast,
162
- );
122
+ await listener.cancel ();
123
+ }
163
124
}
0 commit comments