Can somebody please explain How is whenCompleted() different from then() in Future with example?
Based on my understanding:
WhenCompleted:
This is called before then (Immediately the future completes). You can use this function if you do not need the value returned by the future, but you have to do something after the future is completed. It can also return an error.
Then:
You can use this if you need to access the value of the future and do something after the future function is executed. It can aslo return an error.
These are the only differences I could point out.
Related
I came across this piece of code on SO:
EDIT:the following code snippet is fully functional, I'm trying to understand if beside "working", can it lead to errors due to a possible race condition
if (!_downloaders.containsKey(page)) {
_downloaders[page] = NetworkProvider().getRecentPodcasts(page);
_downloaders[page].then((_) => _downloaders.remove(page));
}
final podcasts = await _downloaders[page];
and I cannot wrap my head around one part:
_downloaders[page].then((_) => _downloaders.remove(page));
here after we added a future to this Map, we execute the future by calling then(),
because we want that after the future has finished, the future is removed from the Map.
Here all is clear, but on the next line, we call await, on the Future that has been added to the Map, and which will be removed soon.
I cannot really understand how this is good code, as it looks to me that when the Future is called with the then(), I know that the code execution doesn't stop for the then() (but it does for await) , but isn't there a remote case where it might get to the await part, but the future is NOT inside the Map anymore, as it has been already removed?
Or this can never happen, and if so, can you explain me the inner workings, so I can fully graps this concept and improve my codebase
I cannot really understand how this is good code, as it looks to me that when the Future is called with the then(), I know that the code execution doesn't stop for the then() (but it does for await) , but isn't there a remote case where it might get to the await part, but the future is NOT inside the Map anymore, as it has been already removed?
Future.then() does not execute the Future's computation. Future.then() only registers a callback.
The cited code usually shouldn't be racy. Futures are normally asynchronous; even if the Future's computation is already complete, the .then() callback will not execute until the Dart runtime returns to the event loop, which in this case would happen at the await line. You can observe this:
void main() async {
print('Constructing Future');
var future = Future.sync(() => print('Ran future'));
print('Constructed Future; registering callback');
// ignore: unawaited_futures
future.then((_) => print('Ran .then() callback'));
print('Registered callback; awaiting');
await future;
print('Done');
}
which will print:
Constructing Future
Ran future
Constructed Future; registering callback
Registered callback; awaiting
Ran .then() callback
Done
It's possible (but unlikely) that the code could be racy in pathological cases. For example, Flutter provides a SynchronousFuture class that implements the Future interface but executes its .then() callback synchronously upon registration. However, that is rather unusual (and which is why the documentation for SynchronousFuture explicitly discourages using it). For that to happen, the NetworkProvider().getRecentPodcasts implementation would have to explicitly return a SynchronousFuture (or some equivalent implementation).
I've wrote a class with some functions that does HTTP calls and returns a Future[String]. I use those functions inside a method that I need to write some tests:
def score(rawEvent: Json) = {
httpService
.get("name", formatJsonAttribute(rawEvent.name))
.onComplete { op =>
op.map { json =>
//What must be tested
}
}
}
The function onComplete doesn't have a return type - it returns Unit. How can I replace that onComplete to make my function return something to be tested?
I completely agree with #Michal, that you should always prefer map to onComplete with Futures. However I'd like to point out that, as you said yourself, what you wish to test is not the HTTP call itself (which relies on an HTTP client you probably don't need to test, a response from a server on which you may have no control, ...), but what you do with its answer.
So why not write a test, not on the function score, but on the function you wrote in your onComplete (or map, if you decided to change it)?
That way you will be able to test it with precise values for json, that you may wish to define as the result you will get from the server, but that you can control completely (for instance, you could test border cases without forcing your server to give unusual responses).
Testing that the two (HTTP call and callback function) sit well together is not a unit-test question, but an integration-test question, and should be done only once you know that your function does what is expected of it.
At that time, you will effectively need to check the value of a Future, in which case, you can use Await.result as #Michal suggested, or use the relevant constructs that your test framework gives. For instance, scalatest has an AsyncTestSuite trait for this kind of issue.
Use map instead of onComplete. It will also provide you with resolved value inside mapping function. The return type of score function will be Future[T] where T will be the result type of your processing.
In the tests you can use scala.concurrent.Await.result() function.
When I use Scala futures I feel confused as to whether I should return a future or return a Try. Since my code could fail, so I expect to return a Try which may have success or failure and the user of the function could use the function to create a future.
Futures can fail, yes, but that failure is self-contained--it's not going to propagate to the rest of your code. Future is very similar to Try in that sense, and in fact the value it holds is Option[Try[T]].
So when you map a Future, you'll only be handling the Success case of it's value, and if you want to handle the failures, you can use recover or recoverTo.
Future callback functions also deal with the Try directly:
Future(...).onComplete {
case Success(value) => ...
case Failure(throwable) =>
}
Stick with Future when you need async results, as it uses Try internally anyway.
If you want to get your result asynchronously you should use Future. Future has 2 types of results: Success and Failure which as you know are descendants of Try, so in any case you'll catch the failre if it happens.
Let's say I was designing yet another actor API. What should I use for result type of send? If I want clients of my code to return back to it's business as far as it possible (in other words, fire-and-forget type of sending) and process response somewhere in the future I would go with the Future. If I want to block until response will come back I will pick Try.
Hope, that makes things a bit clear for you.
I know they have different interfaces but I am wondering what they differ in essence. What would be different if I pass a Future instead of a Try to the complete function of Promise?
You cannot pass a Future to complete. It will not type check.
The intent of completeWith is to complete the promise with the result of given future.
This means it has to wait for the future to complete.
Is there something like Scala's Promise in F#?
While futures are defined as a type of read-only placeholder object
created for a result which doesn’t yet exist, a promise can be thought
of as a writeable, single-assignment container, which completes a
future. That is, a promise can be used to successfully complete a
future with a value (by “completing” the promise) using the success
method. Conversely, a promise can also be used to complete a future
with an exception, by failing the promise, using the failure method.
The Async stuff covers part of this, but if you've got code that works outside the Async environment, Promises are a handy tool. (You can do things like complete a Promise in a UI thread, for example - even when the UI environment knows nothing at all about Async.)
The .Net equivalent of a promise is a TaskCompletionSource, so you can use them from F#. You can create an Async<T> from a Task<T> using Async.AwaitTask e.g.
let tcs = new TaskCompletionSource<int>()
let ta: Async<int> = Async.AwaitTask tcs.Task
//complete completion source using `SetResult`\`SetException` etc.