Как я могу ставить вызовы в очередь асинхронной функции и выполнять их по порядку?

#flutter #dart #dart-async

Вопрос:

У меня есть асинхронная функция, которая выполняет сетевой запрос, и я хочу убедиться, что вызовы функции ожидают завершения любых предыдущих вызовов, прежде чем выполнять сетевой запрос. Я также хочу убедиться, что вызовы функции приводят к сетевым запросам в том же порядке.

Я придумал базовое решение, используя a List<Completer> . Ниже приведен упрощенный пример, который можно запустить на DartPad. Обратите внимание, что пример очень упрощен и просто содержит множество вызовов, чтобы проиллюстрировать идею — в реальном мире вызовы функции могут поступать из разных мест — таймеры, взаимодействие с пользователем и т. Д.

 import 'dart:async';


int i = 0;

List<Completer> funcCompleters = [];
Future<void> func() async{ 
  // Make sure that subsequent calls wait for us 
  Completer c = Completer();
  funcCompleters.add(c);

  // Wait for any previous calls
  if( funcCompleters.length > 1 ) await funcCompleters[funcCompleters.length-2].future;      
  
  // Simulate a lengthy operation, such as a network request
  await Future.delayed(Duration(milliseconds: 500));

  // Generate some output so we can see what happens
  i  = 1;
  print('i: $i   funcCompleters.length: ${funcCompleters.length}');

  // Let any queued calls execute 
  c.complete();
  funcCompleters.remove(c);
}


void main() async {  

  await func(); 
  func(); 
  func(); 
  Timer(Duration(milliseconds: 800), () => func());
  await func(); 
  func(); 
  await func(); 
  await func(); 
  func(); 
  func(); 
  Timer(Duration(milliseconds: 1), () => func());
  func(); 
}
 

Это работает, но неуклюже.

В принципе, идея состоит в том, чтобы поставить функцию в очередь для любого вызова к ней и выполнять вызовы один за другим.

Есть ли лучший способ сделать это?

Комментарии:

1. видеть Future.forEach

2. @pskink Спасибо, хотя мне следовало бы пояснить, что пример кода очень упрощен и что в моем реальном приложении вызовы функции могут исходить из разных мест, таких как таймеры и взаимодействие с пользователем. Я отредактировал вопрос, чтобы было более ясно, что пример очень упрощен.

3. поэтому используйте StreamController и Stream.asyncMap — в документах говорится: «Создает новый поток с каждым событием данных этого потока, асинхронно сопоставленным с новым событием. Это действует как карта, за исключением того, что преобразование может вернуть будущее, и в этом случае этот поток ожидает завершения этого будущего, прежде чем продолжить свой результат».

Ответ №1:

Я думаю, что вам нужно заставить все функции ждать таким образом, чтобы никто не выполнял их раньше предыдущего, и теперь все функции будут выполняться в том же порядке, в котором вам нужно, если это ваша цель из этого вопроса

Ответ №2:

Просто используйте Stream.asyncMap для ожидания фьючерсов по порядку

 import 'dart:async';

final orderedFuturesController = StreamController<Future>();

Future preserveOrder(Future future) {
  orderedFuturesController.add(future);
  return future;
}

Future<void> anyAsyncFunction(int value) async => print(value);

Future<void> main() async {
  final subscription = orderedFuturesController.stream
      .asyncMap((future) async => await future)
      .listen((_) {}, cancelOnError: false);

  await preserveOrder(anyAsyncFunction(0));
  preserveOrder(anyAsyncFunction(1));
  await preserveOrder(anyAsyncFunction(2));
  preserveOrder(anyAsyncFunction(3));

  await orderedFuturesController.stream.isEmpty;
  await subscription.cancel();
}