Reputation: 1783
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
Upvotes: 1
Views: 2664
Reputation: 89946
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 thethen()
(but it does forawait
) , but isn't there a remote case where it might get to theawait
part, but thefuture
is NOT inside theMap
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. Future
s 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).
Upvotes: 4