@@ -62,102 +62,63 @@ class UpdateNotification {
6262 static StreamTransformer <UpdateNotification , UpdateNotification >
6363 filterTablesTransformer (Iterable <String > tables) {
6464 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+ });
6871 }
6972}
7073
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 .
7376///
7477/// Behaviour:
7578/// If there was no event in "timeout", and one comes in, it is pushed immediately.
7679/// Otherwise, we wait until the timeout is over.
7780Stream <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 );
14596
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+ }
155121
156- downstream.onCancel = () {
157- cancelTimer ();
158- return listener.cancel ();
159- };
160- },
161- isBroadcast: input.isBroadcast,
162- );
122+ await listener.cancel ();
123+ }
163124}
0 commit comments