Photo by Levi Jones on Unsplash
Queue and Completer with dart async
this is my coding studying minutes of Queue & Completer in Dart and how to use it.
use case:
I am trying to build an advanced Ble Queue manager that sends data to multiple devices simultaneously and need to understand how Completer and Queue work in depth to have a customized and powerful solution.
What is Queue in Dart? and Why use it?
it is a special kind of Iterator collection that can be manipulated at both ends.
you can add and remove to each end of the stack only (first of the queue and last, no index items access or manipulation).
it can have only one manipulation done at a time, not allowed to modify the queue (add or remove entries) while an operation in the queue is being performed.
it is useful to Implement FIFO 'First in First out ' or LIFO 'Last in First out ' techniques to a list of items or operations using 'removeFirst' & 'removeLast' methods.
It is much more efficient than a list because adding or removing items at the beginning of the list is an O(n) operation because it requires shifting all the other elements. With a
Queue
, adding or removing items at both ends is an O(1) operation, which can be significantly faster for large collections.
Queue Example :
final queue = Queue<int>(); // ListQueue() by default
print(queue.runtimeType); // ListQueue
// Adding items to queue
queue.addAll([1, 2, 3]);
queue.addFirst(0);
queue.addLast(10);
print(queue); // {0, 1, 2, 3, 10}
// Removing items from queue
queue.removeFirst();
queue.removeLast();
print(queue); // {1, 2, 3}
What is Completer in Dart? and Why use it?
a
Completer
in Dart is a way to produceFuture
objects and to complete them later at any point in time.it has a
future
object, this will not complete or run until we callCompleter. Complete
on itthe
Completer
hasa
Completer.complete
method to complete the future and return the resulta
Completer.completeError
method to return an error if the item failed.
u can add callbacks to the end of it to be executed once completed.
it is useful when you're dealing with asynchronous operations that need to be executed in a certain order (like a BLE Queue ).
Completer example:
var completer = Completer<String>(); // Get the future provided by the completer. var future = completer.future; // You can now add callbacks to the future. future.then((value) => print('Received: $value')); // At some point in the future, you can complete the completer. // This will cause the future to complete with the value, // and the callback will be called. completer.complete('Hello, world!');
In this example, when
completer.complete('Hello, world!')
is called, thefuture
immediately completes with the value'Hello, world!'
, and'Received: Hello, world!'
is printed.
Simple TaksQueue examaple :
to make a simple task Queue that executes futures we need to :
have
Queue<Completer>
to hold the items and a method to run each item by callingcomplete
method on it.we have
add
method to add a task to the queue and process itwe have an
_running
bool to skip processing if an item is processed already.call _run on the item that needs to be processed that calls the
Completer.complete
method.then catch any errors or return the value depending on the result of the future.
then remove the task from the queue and start processing the next one.
if we want to have a pause feature for the queue we need :
to have a
paused
bool to determine the state of the queuecheck if the queue is paused before processing any item and return without execution if it is.
add resume method to switch
paused
value and re-process the queue.
here is an example of this Queue logic :
class TaskQueue {
final _queue = Queue<Completer<void>>();
bool _running = false;
bool _paused = false;
Future<void> add(Future<void> Function() task) {
final completer = Completer<void>();
_queue.add(completer);
_run(task, completer);
return completer.future;
}
void pause() {
_paused = true;
}
void resume() {
_paused = false;
if (_queue.isNotEmpty) {
_run(_queue.first, _queue.removeFirst());
}
}
Future<void> _run(Future<void> Function() task, Completer<void> completer) async {
if (_running || _paused) return;
_running = true;
try {
await task();
completer.complete();
} catch (e) {
completer.completeError(e);
} finally {
_queue.remove(completer);
_running = false;
if (!_paused && _queue.isNotEmpty) {
_run(_queue.first, _queue.removeFirst());
}
}
}
}
that's it for now, you can add extra functionality to the queue as you want and share with me your inputs.