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

Я провел много часов в поисках решения, но, поскольку я начинающий пользователь Dart, я не смог найти его самостоятельно. Чего я хочу добиться, так это создать что-то вроде очереди для некоторых асинхронных функций, которые вызываются случайным образом (скажем, когда пользователь нажимает кнопку в моем приложении) из разных точек кода во время работы приложения. Я хочу, чтобы они выполнялись в том порядке, в котором они были вызваны, поэтому в основном у меня есть асинхронные методы, такие как дата обновления() и апдейтпойнтс(), и когда пользователь нажимает кнопку X, будет вызываться updateDate() (добавляется в очередь) и аналогично с Y и updatePoints(). Когда пользователь нажимает i. е. X, X, Y Я хочу запустить updateDate(), updateDate(), updatePoints() именно в этом порядке. Когда одна задача завершена, начинается другая. Думаю, я не могу использовать await для этого. Любые подсказки будут оценены!

если все должно быть синхронизировано, зачем использовать асинхронность? но вы можете попробовать это... doSomethingAsync().then((_) => doAnotherAsync()); Я не уверен, насколько глубоко это будет работать.

stuckedoverflow 09.04.2019 05:06

@stuckedoverflow хорошо, если бы все было синхронизировано, проблем бы не было.

vespi 09.04.2019 10:19

см. forEachAsync - в документах говорится: «Расписание вызовов к действию для каждого элемента в iterable. Одновременно будет ожидать не более maxTasks вызовов к действию».

pskink 09.04.2019 10:36
4
3
1 113
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
import 'dart:async';
import 'dart:collection';
import 'dart:math';

Future<void> main() async {
  _simulateRealWork();
}

Scheduler _scheduler = Scheduler();

class Scheduler {
  bool _scheduled = false;

  Queue<Future Function()> _queue = Queue<Future Function()>();

  void schedule(Future Function() task) {
    _queue.add(task);
    if (!_scheduled) {
      _scheduled = true;
      Timer(Duration(seconds: 0), _execute);
    }
  }

  Future _execute() async {
    while (true) {
      if (_queue.isEmpty) {
        _scheduled = false;
        return;
      }

      var first = _queue.removeFirst();
      await first();
    }
  }
}

void _simulateRealWork() {
  var maxPeriod = 5;
  var count = 5;
  for (var i = 0; i < count; i++) {
    print('Timer $i');
    var random = Random();
    Timer(Duration(seconds: random.nextInt(maxPeriod)), () {
      print('Scheduled work $i');
      Future work() async {
        print('Started work $i');
        await Future.delayed(Duration(seconds: random.nextInt(maxPeriod)));
        print('Ended work $i');
      }

      _scheduler.schedule(work);
    });
  }
}

Результат:

Timer 0 Timer 1 Timer 2 Timer 3 Timer 4 Scheduled work 2 Started work 2 Scheduled work 0 Scheduled work 3 Ended work 2 Started work 0 Scheduled work 1 Scheduled work 4 Ended work 0 Started work 3 Ended work 3 Started work 1 Ended work 1 Started work 4 Ended work 4

Спасибо за ваш ответ. Это именно то, что я искал. Я думаю, что я был довольно близок, хотя.

vespi 09.04.2019 10:28

Следующий код может быть плохой практикой при использовании в большой очереди задач, но если вы уверены, что массив задач не превысит адекватный размер - это может работать нормально:

Future<List<T>> runOneByOne<T>(List<T Function()> list) {
  if (list.isEmpty) {
    return Future.value(null);
  }
  Future task = Future<T>.microtask(list.first);
  final List<T> results = [];

  for (var i = 1; i < list.length; i++) {
    final func = list[i];
    task = task.then((res) { results.add(res); return Future<T>.microtask(func); });
  }

  return task.then((res) { results.add(res); return results; });
}

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

Выполнение останавливается и бросает, если наткнулся на ошибку. В этом случае массив результатов теряется. Вы можете добавить try {...} закрытие к каждой microtask оболочке, чтобы игнорировать ошибки и возвращать null в этой конкретной задаче, сохраняя другие значения в массиве results.

Пример использования:

runOneByOne<int>([
  () { print("First"); return 1; },
  () { print("Second"); return 2; },
  () { print("Third"); return 3; },
]).then((results) {
  print(results); // List<int> [ 1, 2, 3 ]
});

Другие вопросы по теме