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.