Dart equivalent of BlockingCollection - dart

I'm currently migrating an App's logic code from C# to Dart and I'm looking for a similiar collection type in Dart to C#s BlockingCollection. I basically want a queue where i can iterate infinitely. If the queue is empty it just waits until a new element is added.
Is that possible in Dart?
Best

You can use a StreamController.
Here I translated the first C# example for BlockingCollection
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
class BlockingCollectionDemo
{
static async Task Main()
{
await AddTakeDemo.BC_AddTakeCompleteAdding();
}
}
class AddTakeDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.Take()
// BlockingCollection<T>.CompleteAdding()
public static async Task BC_AddTakeCompleteAdding()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
// Spin up a Task to populate the BlockingCollection
Task t1 = Task.Run(() =>
{
bc.Add(1);
bc.Add(2);
bc.Add(3);
bc.CompleteAdding();
});
// Spin up a Task to consume the BlockingCollection
Task t2 = Task.Run(() =>
{
try
{
// Consume consume the BlockingCollection
while (true) Console.WriteLine(bc.Take());
}
catch (InvalidOperationException)
{
// An InvalidOperationException means that Take() was called on a completed collection
Console.WriteLine("That's All!");
}
});
await Task.WhenAll(t1, t2);
}
}
}
to dart using a StreamController instead of BlockingCollection, and Future instead of Task.
import 'dart:async';
Future<void> main() async {
await addTakeCompleteAdding();
}
// Demonstrates:
// StreamController<T>.add()
// StreamController<T>.stream
// StreamController<T>.close()
Future<void> addTakeCompleteAdding() async {
StreamController<int> bc = StreamController<int>();
// Spin up a Future to populate the StreamController
Future<void> t1 = Future(() {
bc.add(1);
bc.add(2);
bc.add(3);
bc.close();
});
// Spin up a Future to consume the StreamController
Future<void> t2 = Future(() async {
// Consume consume the StreamController
await for (final element in bc.stream) {
print(element);
}
// Exits the loop when the stream is completed/closed
print("That's All!");
});
await Future.wait([t1, t2]);
}
That said, the StreamController differs a bit from BlockingCollection in that it is not a queue. A Stream in dart by default, can only have one subscription, unless you create a broadcast stream. Stream is more like an async enumerable in C#.
If you really need a queue data structure you can use the async package, which has a StreamQueue class that you can use to wrap the stream from the StreamController.
Here is the above code modified to use a StreamQueue:
import 'dart:async';
import 'package:async/async.dart';
Future<void> main() async {
await addTakeCompleteAdding();
}
// Demonstrates:
// StreamController<T>.add()
// StreamController<T>.stream
// StreamController<T>.close()
// StreamQueue<T>.next
Future<void> addTakeCompleteAdding() async {
StreamController<int> bc = StreamController<int>();
StreamQueue<int> queue = StreamQueue<int>(bc.stream);
// Spin up a Future to populate the StreamController
Future<void> t1 = Future(() {
bc.add(1);
bc.add(2);
bc.add(3);
bc.close();
});
// Spin up a Future to consume the StreamQueue
Future<void> t2 = Future(() async {
try {
while (true) {
// Consume consume the StreamQueue
print(await queue.next);
}
} on StateError catch (e) {
// A StateError means that next was called on a completed collection
print("That's all!");
}
});
await Future.wait([t1, t2]);
}

You can also write your own queue, based on futures instead of a stream:
import "dart:async" show Completer;
import "dart:collection" show Queue;
abstract class BlockingQueue<T> {
factory BlockingQueue() = _BlockingQueue;
Future<T> removeNext();
void add(T value);
}
class _BlockingQueue<T> implements BlockingQueue<T> {
final Queue<T> _writes = Queue();
final Queue<Completer<T>> _reads = Queue();
Future<T> removeNext() {
if (_writes.isNotEmpty) return Future.value(_writes.removeFirst());
var completer = Completer<T>();
_reads.add(completer);
return completer.future;
}
void add(T value) {
if (_reads.isNotEmpty) {
_reads.removeFirst().complete(value);
} else {
_writes.add(value);
}
}
}
You can also consider a double-blocking queue, where the add method also "blocks" if there is no-one to accept the value yet. It's not even that hard,.
import "dart:async" show Completer;
import "dart:collection" show Queue;
abstract class BlockingQueue<T> {
factory BlockingQueue() = _BlockingQueue;
Future<T> removeNext();
Future<void> add(T value);
}
class _BlockingQueue<T> implements BlockingQueue<T> {
final Queue<T> _writes = Queue();
final Queue<Completer<T>> _completers = Queue();
Future<T> removeNext() {
if (_writes.isNotEmpty) {
assert(_completers.isNotEmpty);
var completer = _completers.removeFirst();
completer.complete(_writes.removeFirst());
return completer.future;
}
var completer = Completer<T>();
_completers.add(completer);
return completer.future;
}
Future<void> add(T value) {
if (_writes.isEmpty && _completers.isNotEmpty) {
var completer = _completers.removeFirst();
completer.complete(value);
return completer.future;
}
var completer = Completer<T>();
_completers.add(completer);
_writes.add(value);
return completer.future;
}
}
That said, if you want to use a for (... in ...)-like loop, you probably do want to go with a Stream and use await for (... in theStream).

Related

Get the list of files from directory

I am trying to add the content of directory using this approach and asign it to List but not sure why it's not working. I see it's async problem but not sure how to solve it.I expect to return the list of files from retCont() but instead it's returning empty list.
import 'dart:io';
void main() async {
print('CONT: ${await retCont()}');
}
Future retCont() async {
var myDir = Directory.current;
List cont = [];
await myDir.list().listen((FileSystemEntity entity) {
print(entity.path);
cont.add(entity.path);
});
return cont;
}
listen() returns a StreamSubscription<FileSystemEntity> which is not a Future so you cannot await on that.
The list() call returns Stream<FileSystemEntity>. For streams you can instead of await and listen() use await for like this:
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() async {
final myDir = Directory.current;
final cont = <String>[];
await for (final entity in myDir.list()) {
print(entity.path);
cont.add(entity.path);
}
return cont;
}
And as a bonus fact, the same program can be written like this if you skip the print(entity.path);.
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() =>
Directory.current.list().map((event) => event.path).toList();
If you really want to use StreamSubscription I think the easiest way is to use a Completer instance which you can complete when you have got all the elements from List():
import 'dart:async';
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() {
final myDir = Directory.current;
final cont = <String>[];
final completer = Completer<List<String>>();
myDir.list().listen((FileSystemEntity entity) {
print(entity.path);
cont.add(entity.path);
}, onDone: () => completer.complete(cont));
return completer.future;
}

How to complete a Future?

I am searching for something like the following, where I return an object from a method, which can be awaited, and completed from its origin function:
Future<dynamic> customFunction() {
// The following class does not exist, but I am searching for a class that would fill that gap.
FutureWithCompletion future = FutureWithCompletion();
() async { // A callback that represents the usecase.
// some computation
future.completeWith('something');
}
return future;
}
/// This function accesses [customFunction].
/// It should print out "something" once the computation is done.
anotherFunction() async => print(await customFunction());
You need to use a Completer:
Future<String> method() {
final completer = Completer<String>();
Timer(Duration(seconds: 5), () => completer.complete('result'));
return completer.future;
}
return Future.value('something`);
otherwise use a Completer https://api.dartlang.org/stable/2.0.0/dart-async/Completer-class.html

Some explanations about completer

Hello everyone
I have one class 'Example' who needs to do some computation. I call start() which call _next(). During the computation _next() calls itself couple of time but in my example I simulate that with a Timer. here is my code
import "dart:async";
main() {
Example ex = new Example();
for (var i = 0 ; i < 3 ; i++) {
ex.start().then((nbr) {
print(nbr);
});
}
}
class Example {
/// for _next
Completer _insideCompleter;
/// from start() to outside
Completer _outsideCompleter;
Example();
/// start is just a better public api than next when we start the exercise
Future<int> start() {
_insideCompleter = new Completer();
_outsideCompleter = new Completer();
_next().then((int value) {
print("value: $value");
_outsideCompleter.complete(value);
}).catchError((message) {
print("information: $message");
});
return _outsideCompleter.future;
}
/// _next handle the flow with the status
Future<int> _next() {
new Timer(new Duration(seconds: 6), () {
_insideCompleter.complete(15);
});
return _insideCompleter.future;
}
}
it finishes with : Bad state: Future already completed. but as you can see in the start(). the Completer are re-created with new. So I don't understand why it is already complete.
If anybody can explain why it's not correct to code like that and maybe give me some interesting links it would be great
Cheers!
I'm not entirely sure what your intention with the code is but I think you should either
Example ex = new Example();
for (var i = 0 ; i < 3 ; i++) { // create a new Example() for each iteration here
ex.start().then((nbr) { // or ensure that the next iteration is not executed
print(nbr); // before the previous is completed.
});
}
with this code ex.start() is called 3 times before the first call is completed.
Here the main issue is about completers called in the callback function
_next().then((int value) {
print("value: $value");
_outsideCompleter.complete(value); // this line
})
and
new Timer(new Duration(seconds: 6), () {
_insideCompleter.complete(15); // this line
});
Because this 2 function are called after your loop and your completer are attribute, all the callback will use the latest _outsideCompleter and _insideCompleter created.
So after than one of the callback have "consume" your completer, the others will create exception of 'Bad state: Future already completed'
here a version that works
import "dart:async";
main() {
Example ex = new Example();
for (var i = 0 ; i < 3 ; i++) {
ex.start().then((nbr) {
print(nbr);
});
}
}
class Example {
Example();
/// start is just a better public api than next when we start the exercise
Future<int> start() {
var outsideCompleter = new Completer(); // create localy each times
_next().then((int value) {
print("value: $value");
outsideCompleter.complete(value);
}).catchError((message) {
print("information: $message");
});
return outsideCompleter.future;
}
/// _next handle the flow with the status
Future<int> _next() {
var insideCompleter = new Completer(); // create localy each times
new Timer(new Duration(seconds: 6), () {
insideCompleter.complete(15);
});
return insideCompleter.future;
}
}

Extending Future

I find quite a lot about using but not about defining futures in Dart. Lets say I have letsWait() which takes quite some time. How do I use the Future class?
import 'dart:async';
void main() {
print('Let\'s get started');
ArtificialWait waitPoint = new ArtificialWait();
Future<String> future = waitPoint.letsWait();
// and how about printing the return here?
print('something fast');
}
class ArtificialWait extends Future<String> {
String letsWait() {
for (var i = 0; i < 5000000000; i++) {
// lol
}
return 'finally';
}
}
This try gives me a:
unresolved implicit call to super constructor 'Future()' class ArtificialWait extends Future<String> {
I don't know why you want to inherit from Future.
Normally you would use this like:
import 'dart:async';
void main() {
print('Let\'s get started');
artificialWait().then((e) => print(e));
// and how about printing the return here?
print('something fast');
}
Future<String> artificialWait () {
var completer = new Completer<String>();
Timer.run(() {
for (var i = 0; i < 5000000000; i++) {
// lol
}
completer.complete('finally');
});
return completer.future;
}
Instead of trying to extend a future, you just need to 'use' the future.
import 'dart:async';
void main() {
print('Let\'s get started');
ArtificialWait waitPoint = new ArtificialWait();
Future<String> future = waitPoint.letsWait();
// and how about printing the return here?
print('something fast');
}
class ArtificialWait {
Future<String> letsWait => new Future<String>(_letsWait);
String _letsWait() {
for (var i = 0; i < 5000000000; i++) {
// lol
}
return 'finally';
}
}
Generally a future can be constructed without using a completer except in certain circumstances. The default constructor for Future will automatically wrap your passed function (which takes no arguments) in a Timer.run() to perform it asynchronously.

Is there any example for dart's `spawnUri(...)` in library "dart:isolate"?

There is a spawnUri(uri) function in dart:isolate, but I don't find any example. I have guessed its usage, but failed.
Suppose there are 2 files, in the first one, it will call spawnUri for the 2nd one, and communicate with it.
first.dart
import "dart:isolate";
main() {
ReceivePort port = new ReceivePort();
port.receive((msg, _) {
print(msg);
port.close();
});
var c = spawnUri("./second.dart");
c.send(["Freewind", "enjoy dart"], port.toSendPort());
}
second.dart
String hello(String who, String message) {
return "Hello, $who, $message";
}
void isolateMain(ReceivePort port) {
port.receive((msg, reply) => reply.send(hello(msg[0], msg[1]));
}
main() {}
But this example doesn't work. I don't know what's the correct code, how to fix it?
Here is a simple example that works with Dart 1.0.
app.dart:
import 'dart:isolate';
import 'dart:html';
import 'dart:async';
main() {
Element output = querySelector('output');
SendPort sendPort;
ReceivePort receivePort = new ReceivePort();
receivePort.listen((msg) {
if (sendPort == null) {
sendPort = msg;
} else {
output.text += 'Received from isolate: $msg\n';
}
});
String workerUri;
// Yikes, this is a hack. But is there another way?
if (identical(1, 1.0)) {
// we're in dart2js!
workerUri = 'worker.dart.js';
} else {
// we're in the VM!
workerUri = 'worker.dart';
}
int counter = 0;
Isolate.spawnUri(Uri.parse(workerUri), [], receivePort.sendPort).then((isolate) {
print('isolate spawned');
new Timer.periodic(const Duration(seconds: 1), (t) {
sendPort.send('From app: ${counter++}');
});
});
}
worker.dart:
import 'dart:isolate';
main(List<String> args, SendPort sendPort) {
ReceivePort receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((msg) {
sendPort.send('ECHO: $msg');
});
}
Building is a two-step process:
pub build
dart2js -m web/worker.dart -obuild/worker.dart.js
See the complete project here: https://github.com/sethladd/dart_worker_isolates_dart2js_test
WARNING : This code is out of date.
Replace your second.dart with the following to make it work :
import "dart:isolate";
String hello(String who, String message) {
return "Hello, $who, $message";
}
main() {
port.receive((msg, reply) => reply.send(hello(msg[0], msg[1])));
}
This gist: https://gist.github.com/damondouglas/8620350 provides a working (I tested it) Dart 1.5 example. An Isolate.spawn(...) example can be found there as well.
Reproducing here (adding import statements):
echo.dart:
import 'dart:isolate';
void main(List<String> args, SendPort replyTo) {
replyTo.send(args[0]);
}
main.dart:
import 'dart:isolate';
import 'dart:async';
main() {
var response = new ReceivePort();
Future<Isolate> remote = Isolate.spawnUri(Uri.parse("echo.dart"), ["foo"], response.sendPort);
remote.then((_) => response.first)
.then((msg) { print("received: $msg"); });
}
shameless copied from
Dart Web Development › Example on how to use Isolate.spawn
I hope the author doesn't mind
The spawned isolate has no idea where/how to respond to its parent.
In the parent, you could create a ReceivePort which will receive all message from child isolates.
Whenever you spawn an isolate, pass it the SendPort instance from your ReceivePort (via the message argument of Isolate.spawn).
The child isolate may/should create its own ReceivePort as well, so it can receive messages.
When instantiated, the child isolate must send its own SendPort (from its own ReceivePort) to its parent (via the parent's SendPort).
The current API is, in its own, really not helpful. But it provides all the necessary building blocks for a full-blown implementation.
You may need to wrap messages inside headers, something along these lines:
class _Request {
/// The ID of the request so the response may be associated to the request's future completer.
final Capability requestId;
/// The SendPort we must respond to, because the message could come from any isolate.
final SendPort responsePort;
/// The actual message of the request.
final dynamic message
const _Request(this.requestId, this.responsePort, this.message);
}
class _Response {
/// The ID of the request this response is meant to.
final Capability requestId;
/// Indicates if the request succeeded.
final bool success;
/// If [success] is true, holds the response message.
/// Otherwise, holds the error that occured.
final dynamic message;
const _Response.ok(this.requestId, this.message): success = true;
const _Response.error(this.requestId, this.message): success = false;
}
Every isolate could have a singleton message bus like this:
final isolateBus = new IsolateBus();
class IsolateBus {
final ReceivePort _receivePort = new ReceivePort();
final Map<Capability, Completer> _completers = {};
IsolateBus() {
_receivePort.listen(_handleMessage, onError: _handleError);
}
void _handleMessage(portMessage) {
if (portMessage is _Request) {
// This is a request, we should process.
// Here we send back the same message
portMessage.responsePort.send(
new _Response.ok(portMessage.requestId, portMessage.message));
} else if (portMessage is _Response) {
// We received a response
final completer = _completers[portMessage.requestId];
if (completer == null) {
print("Invalid request ID received.");
} else if (portMessage.success) {
completer.complete(portMessage.message);
} else {
completer.completeError(portMessage.message);
}
} else {
print("Invalid message received: $portMessage");
}
}
void _handleError(error) {
print("A ReceivePort error occured: $error");
}
Future request(SendPort port, message) {
final completer = new Completer();
final requestId = new Capability();
_completers[requestId] = completer;
port.send(new _Request(requestId, _receivePort.sendPort, message));
return completer.future;
}
}
SendPort anotherIsolatePort = ...
isolateBus.request(anotherIsolatePort, "Some message");
This is just one architectural example. You could of course roll-out your own.
This could be extended to support notifications (requests without response), streams, etc.
A global isolate registry could be needed to keep track of all SendPort instances from every isolates and eventually register them as services.

Resources