Dart: how to manage concurrency in async function
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.
The answers are fine, here's just one more implementation of a "mutex" that prevents async operations from being interleaved.
class AsyncMutex {
Future _next = new Future.value(null);
/// Request [operation] to be run exclusively.
///
/// Waits for all previously requested operations to complete,
/// then runs the operation and completes the returned future with the
/// result.
Future<T> run<T>(Future<T> operation()) {
var completer = new Completer<T>();
_next.whenComplete(() {
completer.complete(new Future<T>.sync(operation));
});
return _next = completer.future;
}
}
It doesn't have many features, but it's short and hopefully understandable.