I have a code like below:
Timer(Duration(seconds: 5),(){
print("This is printed after 5 seconds.");
});
print("This is printed when Timer ends");
How can I use "await" in this case? I want when Timer ends then run next code below Timer. I used Future.delayed(), it can do it but I can't skip the delay time in Future.delayed() like in Timer(). Because i want to skip the delay time if the condition is true. Because I want to skip the delay time if the condition is true. If I use Future.delayed(), it doesn't have cancel() method like Timer().
Please tell me the solution. Thanks
Try Future.delayed instead of Timer
await Future.delayed(Duration(seconds: 5),(){
print("This is printed after 5 seconds.");
});
print('This is printed when Timer ends');
Timer(Duration(seconds: 5),(){
print("This is printed after 5 seconds.");
printWhenTimerEnds();
});
void printWhenTimerEnds(){
print("This is printed when Timer ends");
}
When you wish to skip the timer just call timer cancel and the printWhenTimerEnds() method
There are several ways to achieve what you need. You can use Completer and Future.any like this:
import 'dart:async';
Completer<void> cancelable = Completer<void>();
// auxiliary function for convenience and better code reading
void cancel() => cancelable.complete();
Future.any(<Future<dynamic>>[
cancelable.future,
Future.delayed(Duration(seconds: 5)),
]).then((_) {
if (cancelable.isCompleted) {
print('This is print when timer has been canceled.');
} else {
print('This is printed after 5 seconds.');
}
});
// line to test cancel, comment to test timer completion
Future.delayed(Duration(seconds: 1)).then((_) => cancel());
Basically we are creating two futures one is a delayed and another one is cancelable future. And we are waiting for the first one that completes using Future.any.
The other option is to use CancelableOperation or CancelableCompleter.
For example:
import 'dart:async';
import 'package:async/async.dart';
Future.delayed(Duration(seconds: 1)).then((_) => cancel());
var cancelableDelay = CancelableOperation.fromFuture(
Future.delayed(Duration(seconds: 5)),
onCancel: () => print('This is print when timer has been canceled.'),
);
// line to test cancel, comment to test timer completion
Future.delayed(Duration(seconds: 1)).then((_) => cancelableDelay.cancel());
cancelableDelay.value.whenComplete(() {
print('This is printed after 5 seconds.');
});
Here we do practically the same thing as above but with already available classes. We are wrapping Future.delayed into CancelableOperation so we are able now to cancel that operation (Future.delayed in our case).
Another way you can wrap Timer into future with Completer and so on.
Related
I have this specific code:
Stream<MimeMultipart> mm =
MimeMultipartTransformer(boundary).bind(Stream.fromIterable(l));
// Completer. So we can wait for the two response results.
Completer<dynamic> allComplete = Completer<dynamic>();
List<Future> myAsyncs = [];
myAsyncs.add(allComplete.future);
int listenCalledCount = 0;
mm.listen((event) async {
// this listen event executes multiple times...
Completer<dynamic> listenEventCompleted = Completer<dynamic>();
await Future.delayed(const Duration(seconds: 10));
print("Listening!!");
myAsyncs.add(listenEventCompleted.future);
listenCalledCount++; // How many times onData is called..
}, onDone: () => allComplete.complete(true));
await Future.wait(myAsyncs);
// I want all listen function called to be done first before continuing to the code below..
print(listenCalledCount);
print("All Done!");
My expected results are:
flutter: 3
flutter: All Done!
But what currently happening is:
flutter: 0
flutter: All Done!
What it means is, it doesn't wait for the StreamSubscription OnData event functions which is executed more than once to complete. I want all the OnData event functions to complete before proceeding. Is this possible?
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.)
I needed a single instance of a timer to run on my app such that i can cancel and reinitialise it when needed. However, the timer doesn't cancel after calling the .cancel() operation. It only work if i call it from the default constructor but i want a global timer i can cancel anytime;
Timer timer;
timer = Timer.periodic(Duration(seconds: 10), (Timer t) async {
//cancelling timer only works here e.g (t.cancel)
print("loop operation");
});
timer.cancel(); // calling this method outside the constructor don't work.
I had a similar problem. This happened after I made the callback function async.
I think the solution will differ from problem to problem depending on why your callback is async.
This is not a solution, but rather a hint to get you closer to the solution.
Timer timer;
Timer.periodic(Duration(seconds: 10), (Timer t) async {
timer = t;
print("loop operation");
});
//inside a button callback
timer??.cancel(); // perform null check as well before cancelling.
I'm kinda new to flutter. I searched but couldn't find a suitable answer to this... What would be the best way to call a function if the user doesn't respond to a showDialog alert after x seconds? If the user presses a button, then I don't want the function to execute.
You can start a Timer for x seconds as soon as you display your dialog & then execute your function. If the user clicks your button, you can stop your timer.
Timer _timer;
bool userResponded = false;
You will need a StatefulWidget & wherever you show your dialog, you need to start the timer.
showDialog(...); // Your showDialog method
// You have to update userResponded to true if user clicks on your dialog or whatever
// It should look something like this: setState(() => userResponded = true);
_timer = Timer(const Duration(seconds: 10), () { // Start your timer for x seconds
if (!userResponded) { // If user didn't respond
// execute your function
}
});
Also, you need to override the onDispose method & stop the timer:
#override
void onDispose() {
_timer?.cancel();
super.onDispose();
}
For this, you would use a Timer widget.
Timer example:
Timer _timer = Timer(
const Duration(milliseconds: 500),
() {
// Call some function after delay of 500ms
},
);
To cancel the timer, use _timer.cancel();
So, most likely in the initState method you'll want to set timer object, then when the user presses the button, you can cancel this timer which means the callback in it won't run after a delay you've specified.
I like to know the differences between Future.delayed and Timer method for delaying code execution. Both seem to do the same thing.
Future.delayed
Future.delayed(const Duration(milliseconds: 500), () { /*code*/ });
VS
Timer
Timer _timer = new Timer(const Duration(milliseconds: 500), () { /*code*/ });
A couple of differences for me.
Future.of returns a Future.
Timer does not return anything.
So if your delayed code returns anything that you need to continue your working, Future is the way to go.
Other difference is that the Timer class provides a way to fire repeatedly.
This quote is from the Timer Class Reference documentation itself
A count-down timer that can be configured to fire once or repeatedly
And example to use Timer with the repeat could be
Timer.periodic(Duration(seconds: 5), (timer) {
print(DateTime.now());
});
Other frecuent example is to create a stopwatch, to measure timings in your code, it's usually seen using a Timer.
GL!!
Timer:
Timer() creates a Timer object, that runs your computation after a delay. Since you get the reference to that Timer object, you can choose to cancel it before it's fired, by calling cancel.
Timer t = Timer(Duration(seconds: 1), () => print("1 sec later"));
t.cancel(); // nothing will be printed out
Future:
Future.delayed creates a Future that runs its computation after a delay. Internally, it's still using a Timer to do that. It does not expose the timer to you, so you cannot control or cancel it. On the bright side, you get to do your normal Future stuff, like await on it.
await Future.delayed(Duration(seconds: 1);
print("1 sec later");
Use Timer if:
You want the ability to cancel it. With Timer.cancel() you cancel the timer unlike Future where you'll have to make use of CancelableCompleter to cancel the Future.
If you don't want to return anything in your callback method.
Example:
// Prints 'Hello' after 1s.
var timer = Timer(Duration(seconds: 1), () => print('Hello'));
And in case you decide to cancel it, use:
timer.cancel();
Use Future if:
Your code can throw errors and you want to catch them. Had you used Timer and any uncaught exceptions occurs, the app will exit.
You want to return something from your callback method.
Example:
// Return 'Hello' after 1s and if there is any error, it will be caught.
Future
.delayed(Duration(seconds: 1), () => 'Hello')
.catchError((err) {});
The timer runs its job after the given duration, but flutter not waiting for it to complete its execution, it performs below statements.
Example:
Timer(Duration(seconds: 2), () {
print("Execute this code afer 2 seconds");
});
print("Other code");
Output:
Other code
Execute this code after 2 seconds
So as you can see code below timer will execute first and then the timer will be performed.
Also, Timer can be stopped at any given point before its execution, if we crate the object of it.
Timer timer = Timer(Duration(seconds: 2), () {
print("Execute this code afer 2 seconds");
});
timer.cancel();
The future also runs its job after the given duration, but its return future object means we can use await to get its execution first, and then below statements will be going to execute.
await Future.delayed(Duration(seconds: 2), () {
print("Execute this code afer 2 seconds");
});
print("My Code");
print("Other code");
Output:
Execute this code after 2 seconds
Other code
The main disadvantage of the future is that we can't cancel it in between.