AJ-
AJ-

Reputation: 1783

Flutter: can l call the same Future, first with "then()" followed by "await", without incurring in race condition issues?

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

Answers (1)

jamesdlin
jamesdlin

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 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).

Upvotes: 4

Related Questions