GiorgioFran GiorgioFran - 1 year ago 68
Dart Question

Dart: how to manage concurrency in async function

I really like the async/await pattern in Dart.
It allows me to write readable methods.

But, there are a couple of things that are problematic, one in particular, I don't know hot to manage at all.

The problem is that with async and multiple await inside a method, we introduce concurrency in the method.
For example If I have a method:

Future<int> foo(int value) async {
await foo2();
await foo3();
await foo4();
int ret = foo5(value);
return ret;
}


Well, this is a really simple example.
The problem here is that, for every await, the method is put in the event loop.
That is OK, when you understand it, but this does not prevent your application from calling again the method befor it has retuned a value.

Consider if the method is managing data that is common to the instance of the class and not to the method itself.

So, I have tried the following solution:

bool isWorking = false;

Future<int> foo(int value) async {
if (isWorking) return foo(value);
isWorking = true;

await foo2();
await foo3();
await foo4();
int ret = foo5(value);

isWorking = False;

return ret;
}


As far as I have understood, calling a future method put it immediately in the event loop, so I thought that the execution of the concurrent call of the method was delayed until the first one was ended.
But it is not like that, the program enters in an endless loop.

Anywone can give me an explanation and a solution to this question?

Edit:
in general I think that it could be interesting to have, like in other languages, a synchronized keyword, with the meaning that the method, if called a second time will wait until the first has ended.
Something like:

Future<int> foo(int value) async synchronized {

Answer Source

Instead of a boolean you can use a Future and a Completer to achieve what you want:

Future<Null> isWorking = null;

Future<int> foo(int value) async {
  if (isWorking != null) {
    await isWorking; // wait for future complete
    return foo(value);
  }

  // lock
  var completer = new Completer<Null>();
  isWorking = completer.future;

  await foo2();
  await foo3();
  await foo4();
  int ret = foo5(value);

  // unlock
  completer.complete();
  isWorking = null;

  return ret;
}

The first time the method is call isWorking is null, doesn't enter the if section and create isWorking as a Future that will be complete at the end of the method. If an other call is done to foo before the first call has complete the Future isWorking, this call enter the if section and it waits for the Future isWorking to complete. This is the same for all calls that could be done before the completion of the first call. Once the first call has complete (and isWorking is set to null) the awaiting calls are notified they will call again foo. One of them will be entering foo as the first call and the same workflow will be done.

See https://dartpad.dartlang.org/dceafcb4e6349acf770b67c0e816e9a7 to better see the workflow.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download