i am trying to understand, how dart event loop works. I read the event loop article from the website The Event Loop and Dart and the author explain pretty good how event loop in dart works.
But what i don't understand is, how event get queue. For example
new Future(() => 21)
.then((v) => v*2)
.then((v) => print(v));
Will here dart gonna create three entries in the event queue or just one? I know, that the class Future is responsible for delay execution and when i create an object from it like
new Future(() => 21)
it will be just one entry in the event loop.
In this article, that i have mentioned above, i read about microtask. This microtask is going to execute before event queue, but i don't see any sense, why dart team implement this microtask? Maybe i need some example!
After some investigation it appears that the right answer is "they will be executed in the next event loop"
To test it you can write something like this:
import "dart:async";
void main() {
new Future(() {
scheduleMicrotask(()=>print("before loop 1"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 2"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 3"));
print("in event loop");
});
}
it should output:
in event loop
in event loop
in event loop
before loop 1
before loop 2
before loop 3
Although i'm not sure that you can't break this optimization. So only sure fact is that the firstFuture will complete first and the second - second.
EDIT: The strange part(Obsolete):
With code like this:
import "dart:async";
void main() {
new Future(() {
scheduleMicrotask(print("before loop 1"));
print("in event loop");
}).then((_) {
scheduleMicrotask(print("before loop 2"));
print("in event loop");
}).then((_) {
scheduleMicrotask(print("before loop 3"));
print("in event loop");
});
}
output is:
before loop 1
in event loop
before loop 2
in event loop
before loop 3
in event loop
Unhandled exception:
The null object does not have a method 'call'.
NoSuchMethodError: method not found: 'call'
Receiver: null
...
But with this:
import "dart:async";
void main() {
new Future(() {
scheduleMicrotask(()=>print("before loop 1"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 2"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 3"));
print("in event loop");
});
}
output is:
in event loop
in event loop
in event loop
before loop 1
before loop 2
before loop 3
EDIT2:
I think i got it. In the first(wrong version) scheduleMicrotask actually never got properly scheduled, but since Dart has eager argument execution it executes print() anyway. So what happens is that all Future getting executed in the next event loop and print all text.
That's why output is in call order:
before loop 1
in event loop
before loop 2
in event loop
before loop 3
in event loop
and not in the schedule order.
When you do:
new Future(() => 21)
.then((v) => v*2)
.then(print);
First you call the new Future(...) constructor.
This creates a Future object and schedules a Timer to execute the function you give as argument.
Then you call then. This creates a new future (call it future#2) and adds a listener on the first future. No events are scheduled.
Then you call then again. This creates yet another future (future#3) and adds a listener on the future#2. No events are scheduled.
Then the timer triggers, and the ()=>21 is executed, and the first future is completed with the value 21.
The listener on the first future is then executed immediately. That calls (v)=>v*2 with 21, and then completes future#2 with the value 42.
The listener on future#2 is then executed immediately. That calls print with 42, which prints 42 and returns null. This completes future#3 with the value null.
Future completion is currently done through a "propagate" function that tries to complete as many futures as possible, as long as their listeners are synchronous. That is why completing one future will immediately complete another, without any intermediate microtasks.
The microtask queue is to queue async execution but avoid returning to the main event loop before these microtasks are finished. You can ensure some related activities to be completed entirely even when executed async before other async tasks/events queued in the main queue are executed.
It seems the code executed from then like (v) => v*2 is again executed inside a Future because then always returns a Future.
from https://www.dartlang.org/articles/event-loop/
The microtask queue is necessary because event-handling code sometimes
needs to complete a task later, but before returning control to the
event loop. For example, when an observable object changes, it groups
several mutation changes together and reports them asychronously. The
microtask queue allows the observable object to report these mutation
changes before the DOM can show the inconsistent state.
How I interpret this description doesn't fit with the results in the tests in #Jare 's answer.
Just a little thing to add to previous answers. The 'Event Loop' article explains this behavior pretty well:
The function that you pass into Future’s then() method executes
immediately when the Future completes. (The function isn’t enqueued,
it’s just called.)
(https://www.dartlang.org/articles/event-loop/)
It means that in above examples there's always one event but many microtasks.
Related
import 'dart:async';
void main() async {
Future.microtask(() => print(1));
Future.value(2).then(print);
Future.sync(() => print(3));
Future.sync(() => 4).then(print);
}
Output I observe in dartpad:
3
1
2
4
Why didn't microtask get executed first?And what is different in the two Future.sync functions that printed them in different orders.
An important aspect with the .then() method, is the following you can find in the documentation:
When this future completes with a value, the onValue callback will be called with that value. If this future is already completed, the callback will not be called immediately, but will be scheduled in a later microtask.
https://api.dart.dev/stable/2.15.1/dart-async/Future/then.html
So what happens is that:
You schedule a microtask to call print(1).
Future.value(2) is done in sync and the following .then will be done in a microtask. The queue of microtasks is now: print(1), print(2).
The third line is sync and runs immediately in full so we run print(3). This gives the first line in your input.
We schedule a new microtask because of the .then(). The queue of microtasks is now: print(1), print(2), print(4).
After main() is done we run the microtasks in order they come in which explains the rest of your output.
An important note is that you are never awaiting on any of the returned Future from the .then() methods so any async stuff will first be executed when main() is done.
I'm new to Dart. As i understood from some articles and docs (for example this article):
async/await uses the same mechanism as Future, and that is FIFO Event Loop.
Event Loop launches after the execution of main().
async functions runs synchronously up to the first await keyword. Then they pause the execution, and execution returns to the previous function in call stack.
the remaining code is wrapped to the Future and queued to the Event Loop (most unsure point for me).
Based on these points i expect, that the following code:
main() {
Future(() => print("1"));
Future(() {
print("2");
asyncFunc("2");
});
Future(() => print("3"));
print("main");
asyncFunc("main");
print("\nEvent loop starts?\n");
}
asyncFunc(String from) async {
print("start [from $from]");
await (() async => print("middle [from $from]"))();
print("end [from $from]");
}
Will create something similar to this event queue after main() execution:
future1 -> future2 -> future3 -> end_from_main
And after execution of future2, event end_from_future2 will be queued to the end:
future1 -> future2 -> future3 -> end_from_main -> end_from_future2
So result output i expect should be:
main
start [from main]
middle [from main]
Event loop starts?
1
2
start [from 2]
middle [from 2]
3
end [from main]
end [from 2]
But in fact it returns:
main
start [from main]
middle [from main]
Event loop starts?
end [from main]
1
2
start [from 2]
middle [from 2]
end [from 2]
3
So the conclusion i made: Either async/await events has priority over Fututre. Either they use diffrent mechanism, unrelated to EventLoop. Or maybe i misunderstand something hardly..
What i think i understood:
Future can represent either events in Event queue, either tasks in Microtask queue.
(for example: default Future() constructor puts event to Event queue, Future.microtask() puts task to Microtask queue)
await creates Future.than callback to recived Future.
My topic example explanation:
First three Futrues put events in Event queue.
Future<void> created implicitly in await (() async => print("asyncFunc middle"))() creates "empty" task in Microtask queue (i assume that all implicit Futures are scheduled to Microtask queue). Than await puts remaining instructions (print("end [from main]")) as Future.than callback to this "empty" task.
When it comes to Event Loop it first executes "empty" task from Microtask queue. Which ended with callback: print("end [from main]").
And than Event loop executes Event queue, where second event spawns similar "empty" task in Microtask queue. And that's why end [from 2] happens before third Future
I'm exploring Futures in Dart, and I'm confused about these two methods that Future offers, .then() and .whenCompleted(). What's the main difference between them?
Lets say I want to read a .txt using .readAsString(), I would do it like this:
void main(){
File file = new File('text.txt');
Future content = file.readAsString();
content.then((data) {
print(content);
});
}
So .then() is like a callback that fires a function once the Future is completed.
But I see there is also .whenComplete() that can also fire a function once Future completes. Something like this :
void main(){
File file = new File('text.txt');
Future content = file.readAsString();
content.whenComplete(() {
print("Completed");
});
}
The difference I see here is that .then() has access to data that was returned!
What is .whenCompleted() used for? When should we choose one over the other?
.whenComplete will fire a function either when the Future completes with an error or not, instead .then will fire a function after the Future completes without an error.
Quote from the .whenComplete API DOC
This is the asynchronous equivalent of a "finally" block.
then runs if the future completes successfully.
catchError runs if the future fails.
whenComplete runs regardless of the future completed with a value or with an error.
Here's the basic flow:
someFuture().then((value) {
print('Future finished successfully i.e. without error');
}).catchError((error) {
print('Future finished with error');
}).whenComplete(() {
print('Either of then or catchError has run at this point');
});
.whenComplete = The function inside .whenComplete is called when this future completes, whether it does so with a value or with an error.
.then = Returns a new Future which is completed with the result of the call to onValue (if this future completes with a value) or to onError (if this future completes with an error)
Read detail on API DOC
whenComplete then
I know for sure my brain isn't totally clear on async/await so I need some clarity. I see a lot of examples where await is used on some lines and not others inside a function marked async. For example, I rarely if ever have seen an example like this (I am using the print statement as an example of something simple/basic.):
myFunction() async {
await functionA();
await print("This really long thing that's going to print out.");
await functionB();
await MyExtraClass.lookupSomethingQuickly();
...
}
So the examples I see it's usually something like this:
myFunction() async {
await functionA();
print("This really long thing that's going to print out.");
await functionB();
MyExtraClass.lookupSomethingQuickly();
...
}
So I am wondering if there's just an assumption that simple things will complete in order or if theoretically, putting await in front of each line is what I should be doing in cases where I absolutely need line 1 to follow line 2 to follow line 3, etc... Like what if I absolutely need that print to finish before functionB() goes off?
Essentially I find myself making a judgment call on every line every time I am writing a function with async/await and I never know if my code is working because of good timing and luck or if there would ever be cases that would throw the execution off.
async / await is to make asynchronuos code easier to write, to read and to reason about. Synchronuos code doesn't need such support.
For async programming in Dart see also https://www.dartlang.org/docs/tutorials/futures/
If you take this code example
import 'dart:async' show Future;
void main() {
doSomethingAsync().then((_) => print('afterwards'));
print('at last');
}
Future doSomethingAsync() {
return new Future.delayed(const Duration(seconds: 1), () {
print('done something');
});
}
Try it in DartPad
which prints
at last
done something
afterwards
If you're not familiar with async execution this might be surprising
This is because code passed to Future.delayed() is executed with a delay of 1 second. The Future instance returned by doSomethingAsync() "completes" when the code in Future.delayed() has been executed.
In this line
doSomethingAsync().then((_) => print('afterwards'));
we call .then(...) on the Future returned by doSomethingAsync() and pass a closure (inline function) to .then(...). ((_) => print('afterwards')).
A feature of Future is that it calls the code passed to then(...) after it was completed (in our case when done something was printed after 1 sec delay).
So the execution goes something like
call doSomethingAsync() which schedules a call to print('done something) for later execution and returns a Future
call print('at last'); which just prints at last
after 1 second delay print('done something') is called
the Future returned from doSomethingAsync() is completed
the Future calls `(_) => print('afterwards')
main() ends.
When we use async / await the code looks like
import 'dart:async' show Future;
Future main() async {
await doSomethingAsync();
print('afterwards');
print('at last');
}
Future doSomethingAsync() {
return new Future.delayed(const Duration(seconds: 1), () {
print('done something');
});
}
Try it in DartPad
When run, the output is
done something
afterwards
at last
we also could use async / await in doSomethingAsync() but now we only focus on main()
Now the execution looks like
call doSomething() and wait for the returned Future to complete
print('done something') is executed and the Future completed
the execution of code after await continues
print('afterwards');
print('at last');
This is probably the behavior you expected.
To your original question. await is only necessary when a call returns a Future and you want the following code only be executed when the Future was completed. If the call doesn't return a Future there is nothing to wait for.
await print('xxx') is still valid code. This is to support functions that sometimes do some async work and return a Future but sometimes the don't have async work to do and execute the code immediately and just return afterwards. In this case there is nothing to wait for.
someTimesAsync() {
if(new DateTime.now().weekday == 1) {
return new Future.delayed(const Duration(seconds: 1), () {
print('done something');
});
} else {
print('done something');
}
}
await someTimesAsync();
works in both cases. If it wouldn't this would be cumbersome.
For more details about async / await see also https://www.dartlang.org/articles/await-async/
I need to make a series of database queries that each return a stream of results. Once all the information is collected and sent the 'complete' message needs to be send last. In my code 'sendCompleteMessageToClient' gets sent first.
Future.forEach(centerLdapNames.values, (center) {
db
.collection(center)
.find({'date': {'\$gte': json['from'], '\$lt': json['to']}})
.forEach(sendAppointmentToClient);
}).whenComplete(() => sendCompleteMessageToClient("all"));
How do I wait for all 'sendAppointmentToClient' to finish properly?
I guess you just miss the return of the future
Future.forEach(centerLdapNames.values, (center) {
return db // <== always return the returned future from calls to async functions to keep the chain connected
.collection(center)
.find({'date': {'\$gte': json['from'], '\$lt': json['to']}})
.forEach(sendAppointmentToClient);
}).whenComplete(() => sendCompleteMessageToClient("all"));
If you use wait these calls might be executed in parallel instead of one after the other
Future.wait(centerLdapNames.values.map((center) { ...}, eagerError: false)
.whenComplete((...))