简体   繁体   中英

Force asynchronous code to run synchronously dart

I currently have a class that stores images after fetching images asynchronously over GRPC. There is an issue with the handling of events. The problem can be shown here:

import 'dart:async';

main() {
  IntStore intStore = IntStore();

  print("running getInt(A)");
  intStore.getInt("A");

  print("running getInt(A)");
  intStore.getInt("A");

  print("running getInt(B)");
  intStore.getInt("B");

  print("running getInt(A)");
  intStore.getInt("A");

  print("running getInt(C)");
  intStore.getInt("C");

  print("running getInt(D)");
  intStore.getInt("D");
}

class IntStore {
  final Map _store = <String, int>{};
  Future fetchInt(String intName) async {
    print("Fetching: $intName");
    await doSomeWorkAsynchronously(intName);
  }

  Future<int> getInt(String intName) async {
    if (_store.containsKey(intName)) {
      print("Cached: $intName");
      return _store[intName];
    } else {
      await fetchInt(intName);
      return _store[intName];
    }
  }

  Future doSomeWorkAsynchronously(String intName) async {
    await Future.delayed(const Duration(seconds: 3));
    _store[intName] = 3;
    print("Fetched: $intName");
  }
}

which returns:

running getInt(A)
Fetching: A
running getInt(A)
Fetching: A
running getInt(B)
Fetching: B
running getInt(A)
Fetching: A
running getInt(C)
Fetching: C
running getInt(D)
Fetching: D
Fetched: A
Fetched: A
Fetched: B
Fetched: A
Fetched: C
Fetched: D

The problem here is that the work in fetchInt is completed multiple times. This is quite inefficient.

I found Irn's answer in this question helpful. I implemented this to obtain:

import 'dart:async';

main() {
  IntStore intStore = IntStore();
  AsyncMutex mutex = AsyncMutex();

  print("running getInt(A)");
  mutex.run(() => intStore.getInt("A"));

  print("running getInt(A)");
  mutex.run(() => intStore.getInt("A"));

  print("running getInt(B)");
  mutex.run(() => intStore.getInt("B"));

  print("running getInt(A)");
  mutex.run(() => intStore.getInt("A"));

  print("running getInt(C)");
  mutex.run(() => intStore.getInt("C"));

  print("running getInt(D)");
  mutex.run(() => intStore.getInt("D"));
}

class IntStore {
  final Map _store = <String, int>{};

  Future fetchInt(String intName) async {
    print("Fetching: $intName");
    await doSomeWorkAsynchronously(intName);
  }

  Future<int> getInt(String intName) async {
    if (_store.containsKey(intName)) {
      print("Cached: $intName");
      return _store[intName];
    } else {
      await fetchInt(intName);
      return _store[intName];
    }
  }

  Future doSomeWorkAsynchronously(String intName) async {
    await Future.delayed(const Duration(seconds: 3));
    _store[intName] = 3;
    print("Fetched: $intName");
  }
}

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;
  }
}

which returns

running getInt(A)
running getInt(A)
running getInt(B)
running getInt(A)
running getInt(C)
running getInt(D)
Fetching: A
Fetched: A
Cached: A
Fetching: B
Fetched: B
Cached: A
Fetching: C
Fetched: C
Fetching: D
Fetched: D

where the problem here is that each fetchInt call, while may be necessary, will block each other. This is more inefficient than before. I modified the implementation to be more efficient:

import 'dart:async';

main() {
  IntStore intStore = IntStore();

  print("running getInt(A)");
  intStore.getInt("A");

  print("running getInt(A)");
  intStore.getInt("A");

  print("running getInt(B)");
  intStore.getInt("B");

  print("running getInt(A)");
  intStore.getInt("A");

  print("running getInt(C)");
  intStore.getInt("C");

  print("running getInt(D)");
  intStore.getInt("D");
}

class IntStore {
  final Map _store = <String, int>{};
  final Map _mutexStore = <String, AsyncMutex>{};

  Future<void> fetchInt(String intName) async {
    if (!_store.containsKey(intName)) {
      print("Fetching: $intName");
      await doSomeWorkAsynchronously(intName);
    }
  }

  Future<int> getInt(String intName) async {
    if (_mutexStore.containsKey(intName)) {
      print("Mutex already here: $intName");
      await _mutexStore[intName].run<void>(() => fetchInt(intName));
    } else {
      print("Creating Mutex: $intName");
      _mutexStore[intName] = AsyncMutex();
      await _mutexStore[intName].run<void>(() => fetchInt(intName));
    }
    print("Passing: $intName");
    return _store[intName];
  }

  Future doSomeWorkAsynchronously(String intName) async {
    await Future.delayed(const Duration(seconds: 3));
    _store[intName] = 3;
    print("Fetched: $intName");
  }
}

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;
  }
}

which returns

running getInt(A)
Creating Mutex: A
running getInt(A)
Mutex already here: A
running getInt(B)
Creating Mutex: B
running getInt(A)
Mutex already here: A
running getInt(C)
Creating Mutex: C
running getInt(D)
Creating Mutex: D
Fetching: A
Fetching: B
Fetching: C
Fetching: D
Fetched: A

And will therefore return each int as fast as possible, keeping concurrency between different calls, but blocking repeated calls for the same int.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM