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.
Related
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).
I'm not sure if I'm not understanding this correctly, but here's my code. I'm trying to get the StreamTransformer to act on the stream, but the values still come out the other end untouched.
Note: I added the .map() function, which does nothing, just to make sure it wasn't a missing map function that was my issue. I'm leaving it here just in case.
import 'dart:async';
void main() {
int count = 0;
var counterController = new StreamController();
counterController.stream.listen((value) => print(value));
void increment() {
counterController.add(count++);
}
final transformToString =
new StreamTransformer.fromHandlers(handleData: (number, sink) {
if (number.runtimeType == int) {
sink.add("The counter is at $number!");
} else {
sink.addError("$number is not an int!");
}
});
counterController.stream.map((input) => input).transform(transformToString);
for(int i=0; i < 10; i++){
increment();
}
}
Link to the code in DartPad
As was mentioned by my instructor, the transform function creates out a new stream. So I have to attach a listener to the transformed stream, I can't expect transformed values to come out of the old stream. So the modified code below works.
import 'dart:async';
void main() {
...
counterController.stream.map((input) => input)
.transform(transformToString).listen(print);
for(int i=0; i < 10; i++){
increment();
}
}
Problem
How can I implement the example of Iteration in A Tour of the Dart Libraries?
I understood that it is a code that must be supplemented, but how do I supplement it?
A Tour of the Dart Libraries
class Process {
// Represents a process...
}
class ProcessIterator implements Iterator<Process> {
#override
Process get current => ...
#override
bool moveNext() => ...
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<Process> {
#override
final Iterator<Process> iterator = ProcessIterator();
}
void main() {
// Iterable objects can be used with for-in.
for (var process in Processes()) {
// Do something with the process.
}
}
Development Environment
Dart 2
DartPad
Tried → Error
I read and executed the document on my own, but the following error occurred.
class ProcessIterator implements Iterator<Process> {
#override
Process get current => new Process();
#override
bool moveNext() => false;
}
-> Error: 'IterableBase' expects 0 type arguments.
Best regards,
Two things.
1) Process is not available on the web, so your DartPad example won't work.
2) IterableBase is in dart:collection. Don't forget to import that.
This code seems to work for me:
import 'dart:collection';
class ProcessIterator implements Iterator<int> {
#override
int get current => 0;
#override
bool moveNext() => false;
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<int> {
#override
final Iterator<int> iterator = ProcessIterator();
}
main() {
for (var thing in Processes()) {
print(thing);
}
}
Keep in mind, it's often MUCH easier to use sync* for custom iterators.
Iterable<int> _myProcesses() sync* {
yield 1;
yield 2;
yield 3;
}
main() {
for (var thing in _myProcesses()) {
print(thing);
}
}
Refer to #Kevin 's answer and rewrite the code below.
Code
import 'dart:collection';
class Process {
var foo = 'foo';
}
class ProcessIterator implements Iterator<Process> {
int i = 0;
#override
Process get current => new Process();
#override
bool moveNext() {
if(i++ < 5) {
return true;
} else {
return false;
}
}
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<Process> {
#override
final Iterator<Process> iterator = ProcessIterator();
}
void main() {
// Iterable objects can be used with for-in.
for (var process in Processes()) {
print(process.foo);
}
}
Console
foo
foo
foo
foo
foo
I'm trying to create a server-side Dart class that performs various data-related tasks. All of these tasks rely on the database having been first initialized. The problem is that the init of the database happens asynchronously (returns a Future). I first tried to put the init code into the constructor, but have given up on this approach as it seems to not be viable.
I am now attempting to figure out how to force the DB initialization as a first step in any method call that accesses data. So in other words, when attemptLogin() is called below, I'd like to first check if the DB has been initialized and initialize it if necessary.
However, there are two obstacles. If the database hasn't been initialized, the code is straightforward - initialize the db, then use the then() method of the returned future to do the rest of the function. If the db is not yet initialized, what do I attach my then() method to?
Second related question is what happens when a database is currently being initialized but this process is not yet complete? How can I pull in and return this "in-progress" Future?
This is the basic gist of the code I'm trying to wrangle:
class DataManager {
bool DbIsReady = false;
bool InitializingDb = false;
Db _db;
Future InitMongoDB() {
print("Initializing MongoDB");
InitializingDb = true;
_db = new Db("mongodb://127.0.0.1/test");
return _db.open().then((_) {
DbIsReady = true;
InitializingDb = false;
});
}
Future<List> attemptLogin(String username, String password) {
Future firstStep;
if ((!DbIsReady) && (!InitializingDb) {
Future firstStep = InitMongoDB()
}
else if (InitializingDb) {
// Need to return the InitMongoDB() Future that's currently running, but how?
}
else {
// How do I create a blank firstStep here?
}
return firstStep.then((_) {
users = _db.collection("users");
return // ... rest of code cut out for clarity
});
}
}
Thanks in advance for your help,
Greg
Just return
return new Future<bool>.value(true);
// or any other value instead of `true` you want to return.
// or none
// return new Future.value();
Just keep the future alive:
class DataManager {
Future _initializedDb;
Future initMongoDb() { ... }
Future<List> attemptLogin(String username, String password) {
if (_initializedDb == null) {
_initializedDb = initMongoDB();
}
return _initializedDb.then((db) {
users = db.collection("users");
return // ... rest of code cut out for clarity
});
}
}
You might need to pay attention for the error-case. It's up to you if you want to deal with errors in the initMongoDB or after it.
One of the possible solutions:
import "dart:async";
void main() {
var dm = new DataManager();
var selectOne = dm.execute("SELECT 1");
var selectUsers = dm.execute("SELECT * FROM users");
var users = selectOne.then((result) {
print(result);
return selectUsers.then((result) {
print(result);
});
});
users.then((result) {
print("Goodbye");
});
}
class Event {
List<Function> _actions = new List<Function>();
bool _raised = false;
void add(Function action) {
if (_raised) {
action();
} else {
_actions.add(action);
}
}
void raise() {
_raised = true;
_notify();
}
void _notify() {
if (_actions.isEmpty) {
return;
}
var actions = _actions.toList();
_actions.clear();
for (var action in actions) {
action();
}
}
}
class DataManager {
static const int _STATE_NOT_INITIALIZED = 1;
static const int _STATE_INITIALIZING = 2;
static const int _STATE_READY = 3;
Event _initEvent = new Event();
int _state = _STATE_NOT_INITIALIZED;
Future _init() {
if (_state == _STATE_NOT_INITIALIZED) {
_state = _STATE_INITIALIZING;
print("Initializing...");
return new Future(() {
print("Initialized");
_state = _STATE_READY;
_initEvent.raise();
});
} else if (_state == _STATE_INITIALIZING) {
print("Waiting until initialized");
var completer = new Completer();
_initEvent.add(() => completer.complete());
return completer.future;
}
return new Future.value();
}
Future execute(String query, [Map arguments]) {
return _init().then((result) {
return _execute(query, arguments);
});
}
Future _execute(String query, Map arguments) {
return new Future.value("query: $query");
}
}
Output:
Initializing...
Waiting until initialized
Initialized
query: SELECT 1
query: SELECT * FROM users
Goodbye
I think that exist better solution but this just an attempt to answer on your question (if I correctly understand you).
P.S. EDITED at 11 July 2014
Slightly modified (with error handling) example.
import "dart:async";
void main() {
var dm = new DataManager();
var selectOne = dm.execute("SELECT 1");
var selectUsers = dm.execute("SELECT * FROM users");
var users = selectOne.then((result) {
print(result);
return selectUsers.then((result) {
print(result);
});
});
users.then((result) {
print("Goodbye");
});
}
class DataManager {
static const int _STATE_NOT_INITIALIZED = 1;
static const int _STATE_INITIALIZING = 2;
static const int _STATE_READY = 3;
static const int _STATE_FAILURE = 4;
Completer _initEvent = new Completer();
int _state = _STATE_NOT_INITIALIZED;
Future _ensureInitialized() {
switch (_state) {
case _STATE_NOT_INITIALIZED:
_state = _STATE_INITIALIZING;
print("Initializing...");
new Future(() {
print("Initialized");
_state = _STATE_READY;
// throw null;
_initEvent.complete();
}).catchError((e, s) {
print("Failure");
_initEvent.completeError(e, s);
});
break;
case _STATE_INITIALIZING:
print("Waiting until initialized");
break;
case _STATE_FAILURE:
print("Failure detected");
break;
default:
print("Aleady intialized");
break;
}
return _initEvent.future;
}
Future execute(String query, [Map arguments]) {
return _ensureInitialized().then((result) {
return _execute(query, arguments);
});
}
Future _execute(String query, Map arguments) {
return new Future.value("query: $query");
}
}
For those that are still wondering how to create a blank Future in Dart and later complete them, you should use the Completer class like in the next example.
class AsyncOperation {
final Completer _completer = new Completer();
Future<T> doOperation() {
_startOperation();
return _completer.future; // Send future object back to client.
}
// Something calls this when the value is ready.
void finishOperation(T result) {
_completer.complete(result);
}
// If something goes wrong, call this.
void _errorHappened(error) {
_completer.completeError(error);
}
}
Future<Type> is non nullable in Dart, meaning that you have to initialize it to a value. If you don't, Dart throws the following error:
Error: Field should be initialized because its type 'Future<Type>' doesn't allow null.
To initialize a Future<Type>, see the following example:
Future<String> myFutureString = Future(() => "Future String");
Here "Future String" is a String and so the code above returns an instance of Future<String>.
So coming to the question of how to create a blank/empty Future, I used the following code for initializing an empty Future List.
Future<List> myFutureList = Future(() => []);
I found this link to be quite useful in understanding Futures in Flutter and Dart: https://meysam-mahfouzi.medium.com/understanding-future-in-dart-3c3eea5a22fb
How I can return Future value from Future object?
This code does not work.
import 'dart:async';
void main() {
var temp = foo();
temp.then((Future<int> future) {
future.then((int result) {
print(result);
});
});
}
Future<Future<int>> foo() {
return new Future<Future<int>>(() {
return new Future<int>(() => 5);
});
}
How to prevent unnecessary unwrapping?
In this case in async library 'Future' declared as generic class.
abstract class Future<T> {
}
If I create expression as the following
new Future<Future<int>>();
Then with type T specified as Future<int> which result expected from generic class Future?
I thing that result must be as specified in type argument T.
I.e. Future<int>.
But result is not as expected.
There is no information found about this abnormal behavior on Dart API site.
If this is a "feature" (but I think that abnormal behavior wrongly to call "feature') then why it not documented in Dart API?
How can be explained this discrepancy?
Why this code not generated errors and warnings?
Another IDENTICAL example but w/o using Future.
void main() {
var temp = foo();
temp.baz((Foo<int> foo) {
foo.baz((int result) {
print(result);
});
});
}
Foo<Foo<int>> foo() {
return new Foo<Foo<int>>(() {
return new Foo<int>(() => 5);
});
}
If in this case result will be as when using Future (i.e. unexpected) then how we can call this code?
Normal or abnormal?
Or maybe the Future in Dart some special (magic)?
Look at the api documentation
http://api.dartlang.org/docs/releases/latest/dart_async/Future.html
It says there:
If the returned value is itself a Future, completion of the created future will wait until
the returned future completes, and will then complete with the same result.
I guess that means you can't return a Future from a Future.
But you could return a list of futures.
void main() {
var temp = foo();
temp.then((List<Future<int>> list) {
list[0].then((int result) {
print(result);
});
});
}
Future<List<Future<int>>> foo() {
return new Future<List<Future<int>>>(() {
return [new Future<int>(() => 5)];
});
}
There is no need for any of that extra wrapping. According to the Future documentation:
If the returned value is itself a [Future], completion of the created
future will wait until the returned future completes, and will then
complete with the same result.
This means you can rewrite your code as:
import 'dart:async';
void main() {
var temp = foo();
temp.then((int result) {
print(result);
});
}
Future<int> foo() {
return new Future<int>(() {
return new Future<int>(() => 5);
});
}
This is a lot cleaner to work with and provides the expected result.