Как использовать время динамического интервала в событии Stream.periodic во Flutter

Я изо всех сил пытаюсь найти способ периодически испускать поток с динамическим интервалом времени во Flutter. Я не уверен, это действительно возможно или нет. Одним из обходных путей может быть отмена старого периодического потока и его повторная инициализация с новым временным интервалом, но мой периодический поток с asyncMap не имеет возможности отмены. Я мог бы использовать stream.listen с методом отмены, но мне специально нужен asyncMap для преобразования события Future в поток. В этом случае, что я могу сделать, пожалуйста, предложите мне.

Мой фрагмент кода -

int i = 0;

int getTimeDiffForPeriodicEvent() {
  i++;
  return (_timeDiffBetweenSensorCommands * commandList.length + 1) * i;
}

StreamBuilder(
      stream: Stream.periodic(
              Duration(seconds: maskBloc.getTimeDiffForPeriodicEvent()))
          .asyncMap((_) async => maskBloc.getDataFromMask()),
      builder: (context, snapshot) {
        return Container();
      },
    );

person Nuibb    schedule 17.07.2021    source источник
comment
я не понимаю: вы хотите, чтобы ваш поток периодически выдавал какие-то данные, или вы хотите что-то еще?   -  person pskink    schedule 17.07.2021
comment
Я хочу периодически испускать поток, но через разные промежутки времени. Я имею в виду периодические интервалы времени должны быть динамическими.   -  person Nuibb    schedule 17.07.2021
comment
Или можно отменить Stream.periodic().asyncMap() ?   -  person Nuibb    schedule 17.07.2021
comment
поэтому каждый раз, когда выдается новое значение, это происходит с разными интервалами? например, первое значение через 1 секунду, второе значение через 6 секунд, третье значение через 3 секунды? то есть 1:00:01, 2:00:07, 3:00:10 ?   -  person pskink    schedule 17.07.2021
comment
Вот так. с разными интервалами времени.   -  person Nuibb    schedule 17.07.2021
comment
самое простое решение - генератор async* типа: Stream<int> randomPeriodicStream() async* { int i = 0; while (true) { await Future.delayed(Duration(milliseconds: 1000 + Random().nextInt(3000))); print('yielding $i'); yield i++; } }   -  person pskink    schedule 17.07.2021
comment
или в вашем конкретном случае: randomPeriodicStream() async* { while (true) { await Future.delayed(getTimeDiffForPeriodicEvent()); yield await getDataFromMask(); } } - это все, не нужно Stream.periodic, asyncMap и т. д.   -  person pskink    schedule 18.07.2021
comment
что такое функция randomPeriodicStream() здесь? Не могли бы вы поделиться полным кодом с подключением виджета StreamBuilder?   -  person Nuibb    schedule 18.07.2021
comment
На моей стороне это не работает - StreamBuilder(stream:randomPeriodicStream() async* { int i = 0; while (true) { await Future.delayed(Duration(milliseconds: 1000 + Random().nextInt(3000))); print ('выход $i'); yield i++; } }, builder: (контекст, моментальный снимок) {return Container();});   -  person Nuibb    schedule 18.07.2021
comment
randomPeriodicStream – это асинхронный* генератор. создает Stream, который вы можете использовать непосредственно в StreamBuilder - просто используйте final randomStream = randomPeriodicStream(); и внутри StreamBuilder добавьте stream: randomStream,   -  person pskink    schedule 18.07.2021
comment
конечно, добро пожаловать   -  person pskink    schedule 18.07.2021
comment
Привет, нужна еще одна помощь по выборке фона. Я хочу вызвать эту функцию randomPeriodicStream в фоновом режиме, это действительно возможно?   -  person Nuibb    schedule 29.07.2021


Ответы (1)


Это невозможно с Stream.periodic, но вы, возможно, могли бы создать класс, который может запускать поток и спать на основе некоторой изменяемой переменной, используя async* и yield:

class AdjustablePeriodStream {
  Duration period;
  AdjustablePeriodStream(this.period);

  Stream<void> start() async* {
    while (true) {
      yield null;
      print('Waiting for $period');
      await Future.delayed(period);
    }
  }
}

Это позволит довольно легко изменить период:

Future<void> main() async {
  final ten = Duration(milliseconds: 10);
  final twenty = Duration(milliseconds: 20);
  final x = AdjustablePeriodStream(ten);

  x.start().take(5).listen((_) {
    print('event!');
    x.period = (x.period == ten ? twenty : ten);
  });
}

Вы можете увидеть пример вывода здесь:

https://dartpad.dev/6a9cb253fbf29d8adcf087c30347835c

event!
Waiting for 0:00:00.020000
event!
Waiting for 0:00:00.010000
event!
Waiting for 0:00:00.020000
event!
Waiting for 0:00:00.010000
event!
Waiting for 0:00:00.020000

Он просто переключается между ожиданием 10 и 20 миллисекунд (предположительно, у вас есть какой-то другой механизм, который вы хотите использовать для этого). Возможно, вам также понадобится какой-то способ отменить поток (что позволит выйти из цикла while (true)), но я опустил его здесь, чтобы код был кратким и конкретным.

person Danny Tuppeny    schedule 17.07.2021
comment
Привет Дэнни, Спасибо за ваш отличный комментарий. Но мне интересно, есть ли способ отменить Stream.periodic, который обрабатывается с помощью asyncMap. Вы можете увидеть раздел комментариев в этом посте, где говорится об отмене подписки, возвращаемой 'Stream.listen(...)'. Но мой вопрос в том, можно ли отменить stream.periodic, который имеет дело с asyncMap? stackoverflow .com/questions/51583042/ - person Nuibb; 17.07.2021
comment
Я не уверен, что понимаю - если вы используете asyncMap(), я полагаю, вы все еще где-то вызываете .listen()? В каком случае вы могли бы отменить эту StreamSubscription? - person Danny Tuppeny; 17.07.2021
comment
Нет, я использую StreamBuilder, который прослушивает вещи, которые вы можете видеть в моем фрагменте кода. StreamBuilder(stream: Stream.periodic( Duration(seconds: 5)) .asyncMap((_) async => maskBloc.getDataFromMask()), builder: (context, snapshot) { return Container(); }, ); - person Nuibb; 17.07.2021
comment
Но я не уверен, как в этом случае отменить таймер события Stream.periodic. - person Nuibb; 17.07.2021
comment
См. часть с комментариями в этом фрагменте кода — final stream = Stream.periodic( Duration(seconds: maskBloc.getTimeDiffForPeriodicEvent())) .asyncMap((_) async =› maskBloc.getDataFromMask()); return StreamBuilder(stream: stream, builder: (context, snapshot) { stream.cancel();//Моя проблема: этот метод Cancel не определен для типа stream return Container(); }, ); - person Nuibb; 17.07.2021
comment
Вы должны быть в состоянии использовать что-то вроде кода, который я дал выше. Вы можете добавить метод cancel(), который устанавливает period = null, а затем изменяет цикл where (true) на while (period != null). Теперь вызов cancel() приведет к завершению потока. Затем вам просто нужно сохранить ссылку на экземпляр AdjustablePeriodStream в вашем состоянии и использовать поток, возвращаемый start() в вашем потоковом компоновщике (вы можете вызвать для него asyncMap). - person Danny Tuppeny; 17.07.2021
comment
Привет, нужна еще одна помощь по выборке фона. Я хочу вызвать этот AdjustablePeriodStream в фоновом режиме, это действительно возможно? - person Nuibb; 29.07.2021