I just created button that will run async function. The function will wait 4 seconds and prints.If I click button several times it will print all clicks. I want to after click the button, remove the all previous async functions and leave the last click.This is my following code.
void asyncFunction(){
Timer(Duration(seconds: 4),() async{
print('something');
});
}
So how to finish all previous async functions in flutter?
You need to cancel the previous running timer every time you run your function. Declare a timer into your state as a pointer to the current async execution.
Timer _timer;
void asyncFunction() {
_timer?.cancel();
_timer = Timer(
const Duration(seconds: 4),
() => print('something'),
);
}
declare a Timer to stop it later
Timer timer;
void asyncFunction() {
timer = Timer(Duration(seconds: 1), () async {
print('something');
});
onPressed: () {
if(timer != null)
timer.cancel();
asyncFunction();
}
Related
I have a facade function that reloads the current firebase user and returns it. The thing is that the user reloading part has a timeout and it needs to be tested.
Function:
Future<Option<User>> getSignedInUser() async {
// Reload currentUser if possible
// it mustn't throw [TimeoutException] for whole function,
// this is what this try/catch does
try {
await reloadCurrentUser().timeout(const Duration(seconds: 20));
} catch (e) {
log(e.toString(), name: TAG);
}
return optionOf(_auth.currentUser);
}
reloadCurrentUser() function:
Future<Either<AuthFailure, Unit>> reloadCurrentUser() async {
try {
await _auth.currentUser?.reload();
return right(unit);
} catch (e) {
log(e.toString(), name: TAG);
return left(const AuthFailure.userReloadingError());
}
}
The question is how to test reloadCurrentUser() timeout? I'm trying to throw a TimeoutException when this function is called, but then it throws an error for the whole test.
Current Test function:
test(
'Reaches timeout when reloading currentUser, '
'throws TimeoutException, but function continues '
'and returns optionOf currentUser', () async {
reset(fakeFirebaseAuth);
reset(fakeFacebookAuth);
reset(fakeGoogleSignIn);
final currentUser = FakeUser();
// It says that currentUser exists and *IS* authenticated
when(() => fakeFirebaseAuth.currentUser).thenReturn(currentUser);
when(() => firebaseAuthFacade.reloadCurrentUser())
.thenThrow(TimeoutException('timeout', const Duration(seconds: 20)));
final result = await firebaseAuthFacade.getSignedInUser();
expect(result, isA<Some<User>>());
});
Maybe it's better to remove timeout and use some connectivity package to ensure that the user has a network connection and only then reload the current user?
For testing I'm using mocktail package.
You can use the fake_async package.
Here's a simple example from their docs that you can modify for your use case:
import 'dart:async';
import 'package:fake_async/fake_async.dart';
import 'package:test/test.dart';
void main() {
test("Future.timeout() throws an error once the timeout is up", () {
// Any code run within [fakeAsync] is run within the context of the
// [FakeAsync] object passed to the callback.
fakeAsync((async) {
// All asynchronous features that rely on timing are automatically
// controlled by [fakeAsync].
expect(Completer().future.timeout(Duration(seconds: 5)),
throwsA(isA<TimeoutException>()));
// This will cause the timeout above to fire immediately, without waiting
// 5 seconds of real time.
async.elapse(Duration(seconds: 5));
});
});
}
Is the following code (in an event handler, an OnClicked method) correct or do I need async and await? It "works" but I am unsure if it incorrect and thus a timebomb.
// Set a timer to clear the clipboard
if (State.ClipboardTimeoutSeconds != -1 && State.ClipboardTimeoutSeconds != Int32.MaxValue)
{
Device.StartTimer(new TimeSpan(0, 0, State.ClipboardTimeoutSeconds), () =>
{
Clipboard.SetTextAsync(""); // I guess no need for await because not on UI thread?
return false;
});
}
If I need async and await, how do I deal with the fact that the timer callback must be bool but async lambdas return a Task of some type?
Hello i am working with fire store database for making one to one chat between user. Here i want some pagination of messages. So i called snapshot listener inside initState So the problem is while i navigate to chat screen, initState called snapshot listener DocumentType.added automatically. so data becomes duplicate..
here is my code which i called inside initState
List<DocumentSnapshot> _products = [];
StreamController<List<DocumentSnapshot>> _streamController =
StreamController<List<DocumentSnapshot>>.broadcast();
#override
void initState() {
db
.collection('chat')
.document(docId)
.collection('messages')
.orderBy('timestamp', descending: true)
.snapshots().listen((data) => onChangeData(
data.documentChanges,
));
}
void onChangeData(
List<DocumentChange> documentChanges,
) {
documentChanges.forEach((productChange) {
if (productChange.type == DocumentChangeType.removed) {
_products.removeWhere((product) {
return productChange.document.documentID == product.documentID;
});
_streamController.add(_products);
}
if (productChange.type == DocumentChangeType.added) {
_products.insert(productChange.newIndex, productChange.document);
_streamController.add(_products);
}
if (productChange.type == DocumentChangeType.modified) {
int indexWhere = _products.indexWhere((product) {
return productChange.document.documentID == product.documentID;
});
if (indexWhere >= 0) {
_products[indexWhere] = productChange.document;
}
_streamController.add(_products);
}
});
}
This is normal behavior - when you attach a listener to a document or a collection, you first get a callback with the current data, and then subsequent calls whenever the data is changed.
Because you are using pagination, and attaching a new listener within initState of each of the pages, you get that first callback on each page change.
What you should do is create a separate class that is responsible for listening to your collection and notifying any widgets that should be updated. You can use the state management library of your choice for notifying the widgets of data changes.
How to make a await future not last more than 5 seconds ?
I need it because in some networking operation, the connection is sometimes producing silent error. Hence my client just wait for hours with no response. Instead, I want it trigger an error when the clients waits for more than 5 seconds
My code can trigger the error but it is still waiting
Future shouldnotlastmorethan5sec() async {
Future foo = Future.delayed(const Duration(seconds: 10));;
foo.timeout(Duration(seconds: 5), onTimeout: (){
//cancel future ??
throw ('Timeout');
});
await foo;
}
Future test() async {
try{
await shouldnotlastmorethan5sec(); //this shoud not last more than 5 seconds
}catch (e){
print ('the error is ${e.toString()}');
}
}
test();
When you call Future.timeout you need to use the return value to get the correct behaviour. In your case:
Future shouldnotlastmorethan5sec() {
Future foo = Future.delayed(const Duration(seconds: 10));
return foo.timeout(Duration(seconds: 5), onTimeout: (){
//cancel future ??
throw ('Timeout');
});
}
Suppose you have an async method body, like below, with a method call that returns a Future and a print following it that doesn't not need the result of that method. Is it possible to add some structure to get the print statement to execute independently? Or does this syntactic sugar force an async method body to wholly run sequentially?
Future<String> test2() {
Completer c = new Completer();
new Timer(new Duration(seconds: 2), () {
c.complete('test2: done');
});
return c.future;
}
Future<String> test1() async {
String result = await test2();
print(result);
// Why is this not executed immediately before test2 call completes?
print('hello');
return 'test1: done';
}
void main() {
test1().then((result) => print(result));
}
Follow up: I've added below a rewrite of test1() which has chained async method calls. I'm really after how to use the async syntactic sugar to simplify this type of use case. How could I rewrite this block with the new syntax?
Future<String> test1() async {
test2().then((result) {
print(result);
test2().then((result) {
print(result);
});
});
// This should be executed immediately before
// the test2() chain completes.
print('hello');
return 'test1: done';
}
edit for follow up:
I am not sure what you want to do after all, but if you want wait for the chain to finish before printing things, here's what you have to do:
Future<String> test1() async {
String result;
var result1 = await test2();
print("Printing result1 $result1 ; this will wait for the first call to test2() to finish to print");
var result2 = await test2();
print("Printing result2 $result2 ; this will wait for the second call to test2() to print");
// This should be executed immediately before
// the test2() chain completes.
print('hello');
return 'test1: done';
}
Now if you call test1() and wait for it to return "test1: done" you have to await it.
main() async {
var result = await test1();
print(result); // should print "hello" and "test1: done"
}
If you want to execute code independently from a previous future result, just don't put the await keyword.
Future<String> test1() async {
test2().then((String result) => print(result)); // the call to test2 will not stop the flow of test1(). Once test2's execution will be finished, it'll print the result asynchronously
// This will be printed without having to wait for test2 to finish.
print('hello');
return 'test1: done';
}
The keyword await makes the flow stalled until test2() is finished.
This is because of the await before test2();. The execution is stalled until the Future returned by test2() is completed.