I think I understand the idea behind async, returning a Future, but I am not clear on how async behaves on a very basic level. From my understanding, it does not automatically create asynchronous behavior in a program. For example:
import 'dart:async';
main() {
a();
b();
}
a() {
new Timer(new Duration(milliseconds: 20), () {}); // create latency
print("a");
}
b() {
print("b");
}
// a
// b
If you put async after the a(), the b() is executed first asynchronously, and a() executes after the given latency:
import 'dart:async';
main() {
a();
b();
}
a() **async** {
new Timer(new Duration(milliseconds: 20), () {}); // create latency
print("a");
}
b() {
print("b");
}
// b
// a
But if you put async after both a() and b(), a() executes first, similar to not using async at all:
import 'dart:async';
main() {
a();
b();
}
a() **async** {
new Timer(new Duration(milliseconds: 20), () {}); // create latency
print("a");
}
b() **async** {
print("b");
}
//a
//b
Is using async on all functions cancelling the async function completely?
Now, I think that the main() async does not actually activate the asynchronous behavior by itself. If you add async after the main(), nothing changes. However, it allows for you to use await, in case you must wait for the a() function to finish first, before you continue the program. Is this right?
import 'dart:async';
main() **async** {
**await** a();
b();
}
a() **async** {
new Timer(new Duration(milliseconds: 20), () {}); // create latency
print("a");
}
b() {
print("b");
}
// waits for the value of a() if you put **await** keyword
// a
// b
However, I see main() async {} alot as well as using it after the script html tag, yet there is no await anywhere. Does it mean different things in different contexts? I hope I have explained the logic adequately. Can you explain how I am misunderstanding the use of async/await? Thank you.
a() {
new Timer(new Duration(milliseconds: 20), () {}); // create latency
print("a");
}
This code doesn't delay execution of print("a"); for 20 ms. It just delays the execution of {} which is enqueued for later execution, and then immediately continued with print("a");
Your code using async / await and the equivalent code without async / await would look like:
import 'dart:async';
main() async {
await a();
b();
await main2(); // call the example without async/await
}
Future a() async {
await new Future.delayed(const Duration(milliseconds: 20), () {}); // create latency
print("a");
}
void b() {
print("b");
}
Future main2() {
return a2().then((_) {
b();
});
}
// equivalent of a without async/await
Future a2() {
return new Future.delayed(const Duration(milliseconds: 20), () {}) // create latency
.then((_) => print("a"));
}
Try on DartPad
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 am trying to pass data (bool) from a child class through a callback Function (ondone) provided by the parent class, which will be called in a periodic function with a boolean argument.
import 'dart:async';
class Flow {
MyTimer timer;
bool done = false;
Function ondone;
Flow() {
ondone = (bool b) => done=b;
}
void addtimer(int t) {
timer = MyTimer(t, ondone);
}
}
class MyTimer {
final int time;
int remaining;
Function callback;
Timer _timer;
MyTimer(this.time, this.callback){
remaining = time;
}
void run() {
_timer = Timer.periodic(
Duration(seconds: 1),
(t) {
remaining--;
if (remaining == 0) {
_timer.cancel();
callback(true);
}
});
}
}
But I am unable to figure out if callback is being called or not, because print function (in main) is not printing anything which is wrapped in an if expression.
void main() {
var flow=Flow();
flow.addtimer(5);
flow.timer.run();
if(flow.done) print('Timer Finished..');
print('I need to run while timer is working');
}
Passing data from child to parent in an imperative style is important for me (as a beginner).
The call to flow.timer.run() invokes the Timer which executes asynchronously. Your next line of code tests flow.done immediately, and of course it is not done yet. If you do this:
flow.timer.run();
await Future.delayed(Duration(seconds: 6));
if (flow.done) print('Timer Finished..');
Then your main function will pause for 6 seconds by which time the Timer will be complete.
If you do want to wait for the delay, you could code as follows:
Future<void> run() async {
while (remaining > 0) {
await Future.delayed(Duration(seconds: 1));
remaining = remaining - 1;
}
callback(true);
}
and call it as:
await flow.timer.run();
Edit: If you want to run other code in main and then wait, you can do:
var future = flow.timer?.run();
print('Timer is running...');
await future;
if (flow.done) print('Timer Finished..');
I'm having trouble figuring out a way to do the equivalent of the following Javascript code in Dart:
async function main(){
await new Promise((resolve) => {
setTimeout(resolve, 200);
});
// stuff after 200ms happens here
}
It's nice to be able to create a Promise and resolve it on the fly... can you also do this in dart? I've read about completers, but the examples use "regular" functions, rather than lambda/higher-order ones.
I am not entirely sure what you want but you can write your own example as this in Dart:
import 'dart:async';
Future<void> main() async {
print(DateTime.now().millisecond); // 417
await Future<void>.delayed(const Duration(milliseconds: 200));
print(DateTime.now().millisecond); // 625
}
Maybe a more directly conversion would be:
import 'dart:async';
Future<void> main() async {
print(DateTime.now().millisecond); // 417
await () async {
await Future<void>.delayed(const Duration(milliseconds: 200));
}();
print(DateTime.now().millisecond); // 625
}
The given ECMAScript snippet has an error, setTimeout is supposed to be called on a function, not undefined.
Also, it doesn't really need to be a lambda at all.
function delay_200(resolve) {
setTimeout(resolve, 200);
};
// statically allocated delay function
async function main() {
await new Promise(delay_200);
// stuff after 200ms happens here
}
Now, I haven't used Dart in quite some time, but I think it would be equivalent to the following:
Future<void> main() async {
await Future.delayed(
Duration(milliseconds: 200)
);
// stuff after 200ms happens here
}
Now, I'm honestly not sure if you can construct a Future like you can a Promise, so I didn't really answer the primary question.
Trying to figure a good way to handle nested async functions I'm doing some tests with dartpad. In the following example I don't understand why _Future is printed instead of the value (level1String) ?
The output is :
toto
before await level2
before duration
after duration
13
after level 2
Instance of '_Future' // my concern is here since "after level 2" is printed I should have reached the return statement
after level 1
yeah!
import 'dart:convert';
import "dart:async";
import "dart:html";
void main() async {
print('toto');
await print(level1String());
print('after level 1');
}
Future<String> level1String () async {
print('before await level2');
print(level2String());
print('after level 2');
return 'level1String';
}
int level2String () {
print('before duration');
Timer(Duration(seconds: 3), () {
print('yeah!');
});
print('after duration');
return 13;
}
await print(level1String());
should be
print(await level1String());
level1String() is returning the Future you need to await, not print()
I'd like to run a function over and over, with a delay in between. How can I do this with Dart?
You can use the Timer class to schedule one-shot and repeating functions.
Repeating
Here is how you run a repeating function:
import 'dart:async';
main() {
const oneSec = Duration(seconds:1);
Timer.periodic(oneSec, (Timer t) => print('hi!'));
}
The Timer takes two arguments, a duration and a function to run. The duration must be an instance of Duration. The callback must take a single parameter, the timer itself.
Canceling a repeating timer
Use timer.cancel() to cancel a repeating timer. This is one reason why timer is passed to the callback run from a repeating timer.
One-shot after a delay
To schedule a one-shot function after a delay (execute once, some time in the future):
import 'dart:async';
main() {
const twentyMillis = Duration(milliseconds:20);
Timer(twentyMillis, () => print('hi!'));
}
Notice the callback for a one-shot timer does not take a parameter.
One-shot as soon as possible
You can also request that a function is run as soon as possible, at least one event-loop tick in the future.
import 'dart:async';
main() {
Timer.run(() => print('hi!'));
}
In HTML
Timers even work in HTML. In fact, window.setTimeout was removed, so Timer is the only way to run a function in the future.
5 Sec Timer Example
bool isStopped = false; //global
sec5Timer() {
Timer.periodic(Duration(seconds: 5), (timer) {
if (isStopped) {
timer.cancel();
}
print("Dekhi 5 sec por por kisu hy ni :/");
});
}
Call from any function
sec5Timer();
Stop from any function
isStopped = true;
To dispose you can use this code or technique.
#override
void initState() {
_timer = new Timer.periodic(widget.refreshRate,
(Timer timer) => _updateDisplayTime(inheritedWidget));
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
https://api.dartlang.org/stable/1.24.3/dart-async/Stream/Stream.periodic.html
import 'dart:async';
StreamSubscription periodicSub;
void main() {
periodicSub = new Stream.periodic(const Duration(milliseconds: 500), (v) => v)
.take(10)
.listen((count) => print('tick $count'));
}
or if the counter isn't required just
import 'dart:async';
StreamSubscription periodicSub;
void main() {
periodicSub = new Stream.periodic(const Duration(milliseconds: 500))
.take(10)
.listen((_) => print('tick'));
}
You can also use Future.delayed and await to delay execution:
Future<Null> delay(int milliseconds) {
return new Future.delayed(new Duration(milliseconds: milliseconds));
}
main() async {
await delay(500);
print('Delayed 500 milliseconds');
}
alternative;
import 'dart:async';
Timer interval(Duration duration, func) {
Timer function() {
Timer timer = new Timer(duration, function);
func(timer);
return timer;
}
return new Timer(duration, function);
}
void main() {
int i = 0;
interval(new Duration(seconds: 1), (timer) {
print(i++);
if (i > 5) timer.cancel();
});
}
Opposite to Timer.periodic and Stream.periodic posting my favorite way to handle such a tasks. The advantages:
the first cycle run instantly
the callback can work longer than
interval without any reentrance headache
Completer<bool> periodic(Duration interval, Function(int cycle) callback) {
final done = Completer<bool>();
() async {
var cycle = 0;
while (!done.isCompleted) {
try {
await callback(cycle);
} catch (e, s) {
log("$e", stackTrace: s);
}
cycle++;
await done.future
.timeout(interval)
.onError((error, stackTrace) => null);
}
}();
return done;
}
main() {
final task = periodic(Duration(seconds: 10), (cycle) async {
/// do the periodic tasks here
});
/// main code here
/// and when going to stop the above periodic call
task.complete(true);
}
Functionally identical code to JavaScript (setInterval, setTimeout, clearInterval and clearTimeout):
// ------------------------------
// Import:
import 'dart:async';
// ------------------------------
// Definitions:
void clearTimeout(Timer timer) {
try {
timer.cancel();
} catch (e) {}
}
Timer setTimeout(VoidCallback fn, int millis) {
Timer timer;
if (millis > 0)
timer = new Timer(new Duration(milliseconds: millis), fn);
else
fn();
return timer;
}
void clearInterval(Timer timer) {
try {
timer.cancel();
} catch (e) {}
}
Timer setInterval(VoidCallback fn, int millis) {
Timer timer;
if (millis > 0)
timer = new Timer.periodic(new Duration(milliseconds: millis), (timer) {
fn();
});
else
fn(); // If millis input is too low, only run function once and stop
return timer;
}
// ---------------------------------
// Example:
int myValue = 0;
Timer counter = setInterval((){ myValue++; }, 50);
setTimeout((){
clearInterval(counter);
}, 5000);