What if I have
_asyncMethod();
print("Hi");
The method is executed before print but the method stops at its first await and then print is reached?
OR
The method is NOT executed, the print is reached and then the method is executed, like it was called with a Future.delayed with Duration(seconds:0) call?
_asyncMethod() is executed. As much of it as possible is executed synchronously. That is, _asyncMethod is executed until it reaches its first await (or return statement), which is syntactic sugar for returning a Future to the caller. Since the caller does not itself await the returned Future, the caller resumes executing, which in this case will call print("Hi").
Related
I have a few basic questions about Dart Futures that I can't seem to get an answer to myself. Considering the following code:
Future(
() => print('from Future') // 1
).then(
(_) => print('after Future') // 2
);
What is put on the Event Loop, code block 1 or 2?
If 1 is put on the Event Loop, is 2 executed immediately after it, synchronously, or is it put on the Event Loop as well, for later execution?
If 2 is executed immediately, would it ever make sense for 2 to be:
Future.delayed(someDuration, () => print('after Future'));
What would the use case be? Like to split a longer 'task' so that other code is run in between? Is it something that is actually done in practice, like in Flutter, to prevent 'jank'?
Edit: I found a very insightful article: https://webdev-angular3-dartlang-org.firebaseapp.com/articles/performance/event-loop#how-to-schedule-a-task, which kind of answers pretty much every single question I asked here.
The constructor you are calling is Future() which are documented as:
Creates a future containing the result of calling computation asynchronously with Timer.run.
If the result of executing computation throws, the returned future is completed with the error.
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.
If a non-future value is returned, the returned future is completed with that value.
https://api.dart.dev/stable/2.8.2/dart-async/Future/Future.html
Where Timer.run is documented as:
Runs the given callback asynchronously as soon as possible.
This function is equivalent to new Timer(Duration.zero, callback).
https://api.dart.dev/stable/2.8.2/dart-async/Timer/run.html
So, since we are creating a timer which are already completed, it will immediately be put on the event loop.
So with this knowledge we can answer your questions:
What is put on the Event Loop, code block 1 or 2?
Block 1 is put on the event loop. Since block 2 is dependent on the result from block 1, it will not be put on any queue. Instead, block 2 will be notified when block 1 has returned its result.
If 1 is put on the Event Loop, is 2 executed immediately after it, synchronously, or is it put on the Event Loop as well, for later execution?
As far as I understand the documentation, block 2 will be executed immediately synchronously as part of the block 1 is completed (unless the future as has already been completed which then will trigger a microtask):
Register callbacks to be called when this future completes.
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.8.2/dart-async/Future/then.html
If 2 is executed immediately, would it ever make sense for 2 to be:
The specific example does not make much sense. But yes, you can use the Future.delayed if you want to schedule smaller tasks on the event loop. It should be noted that Dart are single threaded so you cannot schedule tasks to be running in another thread by using Future.delayed.
But in the context of Flutter, you properly want to have multiple smaller tasks so the UI can be drawn between each task. But if you are going to make some heavy calculations, you should properly use an Isolate to run these in another thread.
I was working with flutter, when I made http requests with async and Future, when I executed Future.whenComplete() and printed a message, it is printed infinite number of times, can I stop it's execution
You can register as many then, whenComplete, callbacks to a Future as you want. They all will be called when the Future completes.
If you add such a callback when the Future is already completed, the callbacks will be called immediately (in the next microtask as far as I remember).
In Dart, you can tell the VM to wait for a Future by calling await.
The thing is that you can only call await in an async function, which returns a ... Future.
So if I have a function which doesn't take a long time to run, and has to be run in a function who's type is not async, how do I break out of the async chain?
There is no such thing as breaking out of the async cycle. It's possible to have sync functions to call async code, but the result of the async code won't be available yet when the sync function returns.
The difference between a synchronous function and an asynchronous function is that the former is done when it returns, and the latter is still working in the background when it returns, which is why it returns a Future which will complete when it's really done.
That is the distinction - an asynchronous function is one that returns a Future. The async marker is not what makes the function asynchronous, it's just one way of implementing an asynchronous function. You can also have functions without the async marker which return a Future.
You can call an asynchronous function from a synchronous function. However, there is no way for the synchronous function to delay its return, so it must return before the future completes. It can set up a listener on the future, future.then((value) { doSomethingWith(value); }), but that listener will certainly only be called after the synchronous function has returned. That then-call also returns a future, so the synchronous function has to ignore some Future. That's fine. You are allowed to ignore a future when you don't need the result.
Whatever you do, you can't get the result of a Future in a synchronous function before it returns.
If I run this code:
fanoutObj = fanoutPost(followersList.uid, followers: followersSnap, post: post)
print(fanoutObj)
And the fanoutPost function has a really long for loop in it, then will will all the code in the function including the entire for loop be completed before the print(fanoutObj) runs?
The answer depends on the placement of the for loop:
If the for loop is in the body of the function directly, the answer is "yes", because all statements on the path to the end of the function or to the return statement must complete before the function returns.
If the for loop is in a closure which does not get called in the function itself, gets called asynchronously, or is executed on a separate queue, the answer is "no", because only the registration of the callback must complete, while the callback itself runs separately.
One simple way to find out is to use breakpoints or debug output to check the relative order of execution.
Many long running async methods have completion handler blocks attached as input parameters to them
I'm not sure if the completion handler should be called if the operation was cancelled.
-(void)longRunningAsyncOperation:(Input *)input completionHandler:(Block)completionHandler
{
// long running code
// periodic checks for cancelation
if(_canceled)
{
// should completion handler still be called?
return;
}
// more long running code
// completed
completionHandler(someData);
}
I don't think there's necessarily one "right answer" here. You should just make it do whatever you need to do. One option, as #Fogmeister proposed in a comment is to make the completion routine take an argument indicating whether it was canceled or completed normally. It would seem advisable to have something called in all cases so that an interested party can know that the operation was canceled.
I've seen other APIs that take two different completion blocks -- a "success" block and a "failure" block. To my mind, a single block that takes arguments to indicate status seems like a more adaptable pattern.
If you don't call any completion block on cancelation then there is effectively "lost information"; absent some other mechanism, it's impossible for the outside world to know that the operation was canceled, so it seems like one of these patterns, be it an argument to the completion, or success/failure blocks, would be preferable to simply not calling anything.
#ipmcc is correct. There is generally no one right answer, but best practices generally dictates that you should always call the completion block and, if the completion block actually cares, pass it a success/cancelled flag. The reason this is a best practice is that some memory may have been allocated as a prelude to the cancelled operation and if you don't call the completion handler, you'll never have the opportunity to free it again.
I would say yes, it shall call the completion handler.
The rationale for this is, that one can view a completion handler as some form of return value.
So, not calling the completion handler is like not returning a value in a function which is declared to return something.