I have the following class:
class MyDebounceBuffer<T> {
MyDebounceBuffer({required this.duration});
final Duration duration;
final _controller = StreamController<List<T>>.broadcast();
Stream<List<T>> get stream => _controller.stream;
final _buffer = List<T>.empty(growable: true);
Timer? _timer;
void add(T item) {
_timer?.cancel();
_buffer.add(item);
_timer = Timer(duration, () => emit());
}
void emit() {
_timer?.cancel();
_controller.add(_buffer);
_buffer.clear();
}
}
It is supposed to behave similarly to the debounceBuffer in Pub.dev stream_transform except that it has the option to emit immediately.
When we unit test the code - if fails!
test('MyDebounceBuffer example', () async {
final controller = MyDebounceBuffer<int>(duration: const Duration(milliseconds: 10));
var emitted = false;
controller.stream.listen(expectAsync1(
(p0) {
expect(p0, [0, 1, 2, 3]);
emitted = true;
},
));
controller.add(0);
await Future.delayed(const Duration(milliseconds: 5));
controller.add(1);
await Future.delayed(const Duration(milliseconds: 5));
controller.add(2);
await Future.delayed(const Duration(milliseconds: 5));
controller.add(3);
// Extend the duration of the test to capture the debounceBuffer emission
await Future.delayed(const Duration(milliseconds: 20));
expect(emitted, true);
});
We end up with an empty list being emitted. I can re-write the code to use a new List per event, but I would like to understand what is happening here, or is there a better way to do this?
Do one change in your emit() function to achieve this.
void emit() {
_timer?.cancel();
_controller.add([..._buffer]); // Use this
//_controller.add(List<T>.from(_buffer)); // or this
_buffer.clear();}
Because _controller.add(_buffer);
only send memory address and in next step you used _buffer.clear()
which clear data from list. But List<T>.from(_buffer)
and [..._buffer]
creates a new list in memory.