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

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

Я придумал базовое решение, используя 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(); 
}

Работает, но коряво.

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

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


person Magnus W    schedule 20.03.2021    source источник
comment
см. Future.forEach   -  person pskink    schedule 20.03.2021
comment
@pskink Спасибо, хотя я должен был прояснить, что код примера очень упрощен и что в моем реальном приложении вызовы функции могут происходить из разных источников, таких как таймеры и взаимодействие с пользователем. Я отредактировал вопрос, чтобы было понятнее, что пример очень упрощен.   -  person Magnus W    schedule 20.03.2021
comment
поэтому используйте StreamController и Stream.asyncMap - в документации говорится: Создает новый поток с каждым событием данных этого потока, асинхронно отображаемым на новое событие. Это действует как map, за исключением того, что convert может вернуть Future, и в этом случае этот поток ожидает завершения этого future-объекта, прежде чем продолжить работу со своим результатом.   -  person pskink    schedule 20.03.2021


Ответы (2)


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

person Mohamed Nagdy    schedule 20.03.2021

Просто используйте 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();
}
person Sergey Salnikov    schedule 02.04.2021