How to use dynamic interval time in Stream.periodic event in Flutter - flutter

I'm struggling to find out a way to emit stream periodically with dynamic interval time in Flutter. I'm not sure, is it really possible or not. One workaround may be canceling the old periodic stream and reinitialize it with a new time interval but my periodic stream with asyncMap doesn't have a cancel option. I could use stream.listen that has cancel method but I purposely need asyncMap to convert the Future event to stream. In this case, what I can do please suggest to me.
My code snippet -
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();
},
);

This isn't possible with Stream.periodic, but you could perhaps create a class that can start a stream and sleep based on some mutable variable by using async* and yield:
class AdjustablePeriodStream {
Duration period;
AdjustablePeriodStream(this.period);
Stream<void> start() async* {
while (true) {
yield null;
print('Waiting for $period');
await Future.delayed(period);
}
}
}
This would allow changing the period fairly easily:
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);
});
}
You can see the example output here:
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
It just swaps between waiting 10 and 20 milliseconds (presumably you have some other mechanism you want to use for this). You'd probably also want some way to cancel the stream (which would bail out of the while (true) loop) but I ommitted it here to keep the code short and specific.

Related

Flutter - Waiting for an asynchronous function call return from multiple synchronous function calls

I have an async function which is called multiple times synchoronusly.
List response = await Future.wait([future, future])
Inside, it popups a form and waiting for it to be submitted or cancelled.
var val = await Navigator.push(
context,
MaterialPageRoute(builder : (context) => const TheForm())
);
The first served Future will popup the form first and waiting for the return. No problem with that. But I want the second Future to check first if the form is already popped up. If it is, it just waiting for it to conclude and receive the same returned value.
I'm aware that receiving same function return from two calls sounds crazy and impossible. I'm just looking for a way to hold the second Future call on and trigger to conclude it from somewhere else.
Kindly tell me what I was missing and I'll provide the required information.
I try to use ValueNotifier's. Unfortunately ValueNotifier.addListener() only accept a VoidCallback. As for now, this is my solution. Still looking for a better way to replace the loop.
Future future() async{
if(ison) await Future.doWhile(() async {
await Future.delayed(Duration(seconds: 1));
return ison;
});
else{
ison = true;
result = ... //Popup form
ison = false;
}
return await result;
}
It sounds like you want to coalesce multiple calls to an asynchronous operation. Make your asynchronous operation cache the Future it returns and make subsequent calls return that Future directly. For example:
Future<Result>? _pending;
Future<Result> foo() {
if (_pending != null) {
return _pending!;
}
Future<Result> doActualWork() async {
// Stuff goes here (such as showing a form).
}
return _pending = doActualWork();
}
Now, no matter how many times you do await foo();, doActualWork() will be executed at most once.
If you instead want to allow doActualWork() to be executed multiple times and just to coalesce concurrent calls, then make doActualWork set _pending = null; immediately before it returns.

Firestore requests not working properly in Time.periodic

I want to write firestore database every 15 seconds. This is the code I have right now:
void startTimer() {
const oneSec = const Duration(seconds: 15);
_timer = new Timer.periodic(
oneSec,
(Timer timer) async {
await games.doc(documentNumber).update({
'players': FieldValue.arrayUnion([player.toString()]),
})
},
);
}
Then I call startTimer() in my build function. It writes data to the database, but the performance is weird. Instead of writing the database every 15 seconds, it does it almost every second, and then the number of writes is unpredictable (more than one), which then leads to a memory leak. I don't know what and where to troubleshoot, because I don't see any problem in my code
How can I make it write exactly 1 entry to the database every 15 seconds?
I can't say for sure, but from the context you've given in your question I think I have a good idea of what's going on.
you're calling the timer from your build function
you used to have const oneSec = const Duration(seconds: 1); - either that or your variable naming needs some work
you're never cancelling the timer
So there's a few things going on here. The most obvious issue is that the widget's build function can and will run more than once in almost all applications. You should never rely on it not running more than once - that's not what it is meant for.
Because you initially started your widget with a timer running once a second, that will continue to run indefinitely. If you then do multiple build updates, that would then create more timers which also run indefinitely - which is exactly why you were seeing the number of writes grow as well as the unexpected timing.
Instead, you should be making your widget into a StatefulWidget and initiating the timer in an overridden initState() method (which will only run once per widget but should not access the context, which is fine in your case).
This would look like this:
class MyWidgetState extends State<MyWidget> {
late final Timer _timer;
#override
initState() {
super.initState();
startTimer();
}
#override
dispose() {
stopTimer();
super.dispose();
}
void startTimer() {
const oneSec = const Duration(seconds: 15);
_timer = new Timer.periodic(
oneSec,
(Timer timer) async {
await games.doc(documentNumber).update({
'players': FieldValue.arrayUnion([player.toString()]),
})
},
);
}
void stopTimer() {
_timer.cancel();
}
}
If you did need to access the context, you'd instead need to use an overridden didChangeDependencies() method which can run more than once but likely won't unless you're using inherited widgets. In this case you would need to put in some protections to make sure the timer wasn't created more than once i.e. make it nullable and check if it is set before creating a new one.
Or if you really do need to call the startTimer() function from your build function, you could do it the same way as mentioned above - make _timer nullable and check if it is set before making a new one. But I'd recommend avoiding this - if you always keep things that are started in initState and then always stop them in dispose, it will be a lot easier to figure out what's going on in the class.
I would say that using an asynchronous method inside a Timer could be the problem, However since you want to execute your method every 15 seconds, think about making a recursion method, with Future.dalayed like this:
int updateCount = 0; // this will allow us to track how many times the method is executed
recursionTimerUpdate() async {
await Future.delayed(Duration(seconds: 15)); //
updateCount += 1;
await games.doc(documentNumber).update({
'players': FieldValue.arrayUnion([player.toString()]),
});
bool shouldContinueWorking = updateCount < 10;
if (shouldContinueWorking) {
await recursionTimerUpdate();
}
}
this is a recursion method, that will execute your update() method every 15 seconds, and will stop from continuing after 10 times it's run, If you want it to not stop in your app, just set shouldContinueWorking = true.

How to make a delayed future cancelable in Dart?

Lets say that in Dart/Flutter you have the following code:
void myOperation() {
// could be anything
print('you didn't cancel me!');
}
Notice that the operation itself is not asynchronous and is void -- does not return anything.
We want it to execute at some point in the future, but we also want to be able to cancel it (because a new operation has been requested that supersedes it).
I've started by doing this:
Future.delayed(Duration(seconds: 2), myOperation())
... but this is not cancellable.
How exactly could you schedule that "operation," but also make it cancelable?
I'm thinking... we could modify the code like so:
Future.delayed(Duration(seconds: 2), () {
if (youStillWantThisToExecute) {
print('you didn't cancel me!');
}
});
But that's not really very good because it depends on a "global" boolean... and so if the boolean gets flipped to false, no operations will complete, even the most recently requested, which is the one we want to complete.
It would be nicer if there were a way to create any number of instances of the operation and cancel them on an individual basis... or to have a unique id assigned to each operation, and then instead of having a boolean control whether or not to execute... to have a "mostRecentId" int or something which is checked prior to execution.
Anyways...
CancelableOperation seemed promising just from its name.
So, I looked at its documentation:
CancelableOperation.fromFuture(Future inner, {FutureOr onCancel()})
Creates a CancelableOperation wrapping inner. [...] factory
But honestly that just makes my poor head hurt oh so much.
I've consulted other articles, questions, and answers, but they are all part of some specific (and complex) context and there isn't a dirt simple example anywhere to be found.
Is there a way to make a delayed future cancellable by wrapping it in some other class?
Can someone more experienced please provide at least one simple, complete, verified example that compiles in DartPad?
Thanks.
Use Timer:
var t = Timer(Duration(seconds: 400), () async {
client.close(force: true);
});
...
t.cancel();
Using CancalableOperation will not stop print('hello'); from executing even if you cancel. What it does is canceling(discarding) the result(void in your case). I will give you 2 examples using CancalableOperation and CancalableFuture.
CancelableOperation example
final delayedFuture = Future.delayed(
Duration(seconds: 2),
() {
return 'hello';
},
);
final cancellableOperation = CancelableOperation.fromFuture(
delayedFuture,
onCancel: () => {print('onCancel')},
);
cancellableOperation.value.then((value) => {
// Handle the future completion here
print('then: $value'),
});
cancellableOperation.value.whenComplete(() => {
print('onDone'),
});
cancellableOperation.cancel(); // <- commment this if you want to complete
CancelableFuture example
final delayedFuture = ...;
final cancalableFuture = CancelableFuture<String>(
future: delayedFuture,
onComplete: (result) {
// Use the result from the future to do stuff
print(result);
},
);
cancalableFuture.cancel(); // <- commment this if you want to complete
And the CancelableFuture implementation
class CancelableFuture<T> {
bool _cancelled = false;
CancelableFuture({
#required Future<dynamic> future,
#required void Function(T) onComplete,
}) {
future.then((value) {
if (!_cancelled) onComplete(value);
});
}
void cancel() {
_cancelled = true;
}
}
You cannot cancel an existing Future. If you do:
Future.delayed(
Duration(seconds: 2),
() {
print('hello');
},
);
as long as the process runs (and is processing its event queue) for at least 2 seconds, the Future eventually will execute and print 'hello'.
At best you can cause one of the Future's completion callbacks to fire prematurely so that callers can treat the operation as cancelled or failed, which is what CancelableOperation, et al. do.
Edit:
Based on your updated question, which now asks specifically about delayed Futures, you instead should consider using a Timer, which is cancelable. (However, unlike a Future, callers cannot directly wait on a Timer. If that matters to you, you would need to create a Completer, have callers wait on the Completer's Future, and let the Timer's callback complete it.)

How do I make a Stream version of Future.wait in Dart?

I want to run Future.wait() on some network calls so they can run concurrently. I want to speed this up even further my returning the results as they become available so I can start displaying them. I figured just a small modification to the Future.wait call would be all I need, but unfortunately, it uses private classes internally. Is there a better way to accomplish this?
Future.wait dart documentation
You can use Stream.fromFutures constructor
The stream reports the results of the futures on the stream in the
order in which the futures complete. Each future provides either a
data event or an error event, depending on how the future completes.
If some futures have already completed when Stream.fromFutures is
called, their results will be emitted in some unspecified order.
When all futures have completed, the stream is closed.
Stream<int> streamingInts = Stream.fromFutures([
Future.delayed(Duration(seconds: 1), () => 1),
Future.delayed(Duration(seconds: 2), () => 2),
]);
await for (final i in streamingInts) {
print(i);
}
My real use case - to show that loading takes more time than expected
if my real api call ends fast I just yield LoadedState & stop listen to stream
if my real api call is too slow special Future.delayed will fire special state LoadingTakesTooLongState
Stream<BlocState> loadingStates = Stream.fromFutures([
Future.delayed(_kTooSlowDelay, () => LoadingTakesTooLongState()),
_loadData(),
]);
await for (var state in loadingStates) {
yield state;
if (state is LoadedState) break;
}
Future<LoadedState> _loadData() ...

Flutter setState() doesn't always call my build method

I'm trying out Flutter, but I'm having trouble getting the UI to update consistently. I'd like to show a status message while a long-running async method is called, but the setState() call I make just before calling the long-running method doesn't seem to cause my build() method to get invoked.
I've created a simple example that calculates the Fibonacci number for a randomly selected number between 25 and 30. In my sample code/app, hitting the "calc" button calls _calc(). _calc() picks a random number, sets a status message "Calculating Fib of $num..." tied to a text widget (_status) and updates it with setState(); then calls the async _fib() routine to calculate the number; then updates _status with the result using setState(). Additionally, the build() method prints the value of _status to the console, which can be used to see when build() is invoked.
In practice, when the button is pressed, the first status message does not appear either in the debug console, or on the UI. Doing a bit of experimentation, I added a pseudo sleep function that I call just prior to calling _fib(). This sometimes causes the first setState() call to work properly - invoking build(). The longer I make the sleep, the more often it works. (I'm using values from a few milliseconds up to a full second).
So my question are: What am I doing wrong? and What's the right way to do this? Using the pseudo sleep is obviously not the correct solution.
Other, probably not too relevant info: My dev environment is Android Studio 3.1.2 on a Win10 machine. Using Android SDK 27.0.3, with Flutter beta 0.3.2. My target device is the emulator for a pixel2 running Android 8.1. Also, sorry if my lack of 'new' keywords is off-putting, but from what I read in Dart 2 release notes, it's not usually necessary now.
import 'package:flutter/material.dart';
import "dart:async";
import "dart:math";
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Debug Toy',
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget {
#override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
String _status = "Initialized";
final rand = Random();
Future sleep1() async {
return new Future.delayed(const Duration(milliseconds: 100),() => "1");
}
Future<Null> _resetState() async {
setState(() { _status = "State Reset"; });
}
Future<Null> _calc() async {
// calculate something that takes a while
int num = 25 + rand.nextInt(5);
setState(() { _status = "Calculating Fib of $num..."; });
//await sleep1(); // without this, the status above does not appear
int fn = await _fib(num);
// update the display
setState(() { _status = "Fib($num) = $fn"; });
}
Future<int> _fib(int n) async {
if (n<=0) return 0;
if ((n==1) || (n==2)) return 1;
return await _fib(n-1) + await _fib(n-2);
}
#override
Widget build(BuildContext context) {
print("Build called with status: $_status");
return Scaffold(
appBar: AppBar(title: Text('Flutter Debug Toy')),
body: Column(
children: <Widget>[
Container(
child: Row(children: <Widget>[
RaisedButton( child: Text("Reset"), onPressed: _resetState, ),
RaisedButton( child: Text("Calc"), onPressed: _calc, )
]),
),
Text(_status),
],
),
);
}
}
Let's start by going to one extreme and rewriting fib as fibSync
int fibSync(int n) {
if (n <= 0) return 0;
if (n == 1 || n == 2) return 1;
return fibSync(n - 1) + fibSync(n - 2);
}
and calling that
Future<Null> _calc() async {
// calculate something that takes a while
int num = 25 + rand.nextInt(5);
setState(() {
_status = "Calculating Fib of $num...";
});
//await Future.delayed(Duration(milliseconds: 100));
int fn = fibSync(num);
// update the display
setState(() {
_status = "Fib($num) = $fn";
});
}
The first setState just marks the Widget as needing to be rebuilt and (without the 'sleep') continues straight into the calculation, never giving the framework the chance to rebuild the Widget, so the 'Calculating' message isn't displayed. The second setState is called after the calculation and once again (redundantly) marks the Widget as needing to be rebuilt.
So, the execution order is:
Set status to Calculating, mark Widget as dirty
Perform the synchronous calculation
Set status to Result, mark Widget as dirty (redundantly)
Framework finally gets chance to rebuild; build method is called
When we uncomment the 'sleep', the execution order changes to
Set status to Calculating, mark Widget as dirty
'Sleep', allowing the framework to call build
Perform the synchronous calculation
Set status to Result, mark Widget as dirty (again)
Framework calls build
(As an aside, note how the synchronous fib calculation is an order of magnitude faster because it doesn't have to do all the microtask scheduling.)
Let's re-consider the async calculation. What's the motivation of making it async? So that the UI remains responsive during the calculation? As you've seen, that doesn't achieve the desired effect. You still only have one thread of execution, and you aren't allowing any gaps in execution for callbacks and rendering to occur. Sleeping for 100ms is not compute bound, so drawing etc can occur.
We use async functions to wait for external events, like replies from web servers, where we don't have anything to do until the reply arrives, and we can use that time to keep rendering the display, reacting to gestures, etc.
For compute bound stuff, you need a second thread of execution which is achieved with an Isolate. An isolate has its own heap, so you have to pass it its data, it works away in its own space, then passes back some results. You can also stop it, if it's taking too long, or the user cancels, etc.
(There are much less computationally expensive ways to calculate fibs, but I guess we're using the recursive version as a good example of an O(n^2) function, right?)