Why does this test using FakeAsync just hang on the "await" even though the future has completed? - flutter

I'm trying to write a test using FakeAsync but it seems to hang on my awaits. Here's a stripped down example:
test('danny', () async {
await FakeAsync().run((FakeAsync async) async {
print('1');
final a = Future<bool>.delayed(const Duration(seconds: 5))
.then((_) => print('Delayed future completed!'))
.then((_) => true);
print('2');
async.elapse(const Duration(seconds: 30));
// Tried all this too...
// async.flushMicrotasks();
// async.flushTimers();
// async.elapse(const Duration(seconds: 30));
// async.flushMicrotasks();
// async.flushTimers();
// async.elapseBlocking(const Duration(seconds: 30));
print('3');
await a;
print('4');
expect(1, 2);
});
});
This code outputs:
1
2
Delayed future completed!
3
// hangs and never prints '4'
The async.elapse call is allowing the future to be completed, but it still hangs on await a. Why?

This seems to occur because although the Future is completed, the await call requires the microtask queue to be processed in order to continue (but it can't, since nobody is calling async.elapse after the await).
As a workaround, contiually pumping the microstask queue while the function is running seems to work - for example calling this function in place of FakeAsync.run:
/// Runs a callback using FakeAsync.run while continually pumping the
/// microtask queue. This avoids a deadlock when tests `await` a Future
/// which queues a microtask that will not be processed unless the queue
/// is flushed.
Future<T> runFakeAsync<T>(Future<T> Function(FakeAsync time) f) async {
return FakeAsync().run((FakeAsync time) async {
bool pump = true;
final Future<T> future = f(time).whenComplete(() => pump = false);
while (pump) {
time.flushMicrotasks();
}
return future;
}) as Future<T>;
}

It seems that await does not work inside fakeAsync, so you are stuck with Future.then() and friends.
However you can very easily wait for all Futures to complete by calling time.elapse()and time.flushMicrotasks().
test('Completion', () {
fakeAsync((FakeAsync time) {
final future = Future(() => 42);
time.elapse(Duration.zero);
expect(future, completion(equals(42)));
time.flushMicrotasks();
});
});

Related

In flutter, how to prevent control being given back to caller after first await [duplicate]

Sample code:
Map<String, String> gg = {'gg': 'abc', 'kk': 'kojk'};
Future<void> secondAsync() async {
await Future.delayed(const Duration(seconds: 2));
print("Second!");
gg.forEach((key, value) async {
await Future.delayed(const Duration(seconds: 5));
print("Third!");
});
}
Future<void> thirdAsync() async {
await Future<String>.delayed(const Duration(seconds: 2));
print('third');
}
void main() async {
secondAsync().then((_) {
thirdAsync();
});
}
output
Second!
third
Third!
Third!
as you can see i want to use to wait until foreach loop of map complete to complete then i want to print third
expected Output
Second!
Third!
Third!
third
Iterable.forEach, Map.forEach, and Stream.forEach are meant to execute some code on each element of a collection for side effects. They take callbacks that have a void return type. Consequently, those .forEach methods cannot use any values returned by the callbacks, including returned Futures. If you supply a function that returns a Future, that Future will be lost, and you will not be able to be notified when it completes. You therefore cannot wait for each iteration to complete, nor can you wait for all iterations to complete.
Do NOT use .forEach with asynchronous callbacks.
Instead, if you want to wait for each asynchronous callback sequentially, just use a normal for loop:
for (var mapEntry in gg.entries) {
await Future.delayed(const Duration(seconds: 5));
}
(In general, I recommend using normal for loops over .forEach in all but special circumstances. Effective Dart has a mostly similar recommendation.)
If you really prefer using .forEach syntax and want to wait for each Future in succession, you could use Future.forEach (which does expect callbacks that return Futures):
await Future.forEach(
gg.entries,
(entry) => Future.delayed(const Duration(seconds: 5)),
);
If you want to allow your asynchronous callbacks to run concurrently (and possibly in parallel), you can use Future.wait:
await Future.wait([
for (var mapEntry in gg.entries)
Future.delayed(const Duration(seconds: 5)),
]);
See https://github.com/dart-lang/linter/issues/891 for a request for an analyzer warning if attempting to use an asynchronous function as a Map.forEach or Iterable.forEach callback (and for a list of many similar StackOverflow questions).

Return code results after some delay in Flutter?

I'm trying to run this Future function that runs a Timer once 500 milliseconds have passed. The issue I'm having is that the timer is not passing the data to the results variable so the _destinationOnSearchChanged() ends up returning null.
Note: getCity(context, _typeAheadController.text); does return data but only inside the Timer function.
Future _destinationOnSearchChanged() async {
dynamic results;
//Cancels timer if its still running
if (_apiCityThrottle?.isActive ?? false) {
_apiCityThrottle.cancel();
}
//Makes API call half a second after last typed button
_apiCityThrottle = Timer(const Duration(milliseconds: 500), () async{
results = await getCity(context, _typeAheadController.text);
});
print(results);
return await results;
}
As pskink noted in a comment, you probably should look into existing debounce mechanisms instead of creating your own.
If you still want to proceed down this path: your problem is that create a Timer and then return results immediately. You don't wait for the Timer to fire (and you can't directly await a Timer). In this case, you could use a Completer:
Future _destinationOnSearchChanged() async {
var timerCompleter = Completer<dynamic>();
// Cancels timer if it's still running.
_apiCityThrottle?.cancel();
// Makes API call half a second after last typed button.
_apiCityThrottle = Timer(const Duration(milliseconds: 500), () async {
timerCompleter.complete(await getCity(context, _typeAheadController.text));
});
var results = await timerCompleter.complete();
print(results);
return results;
}

How to use Future Delayed

I have 2 functions. I want to run them one by one but while the first function is done, the second function must wait for 1-2 seconds. I tried Future.delayed for this but it did not work. It changes nothing.
void kartat(int tip, int deger, int mainid, List mycards) {
masadakicards.add(cardbank[mainid]);
print("kart atıldı");
rakipkartat(51);
}
void rakipkartat(int mainid) {
new Future.delayed(Duration(seconds: 1), () {
// deleayed code here
masadakicards.add(cardbank[mainid]);
print("ann");
});
}
A way that you can achieve this is by using await Future.delayed
make sure that your method is returns a Future
Future<void> start() async {
await foo();
await Future.delayed(Duration(seconds: 2));
await bar();
}
Future<void> foo() async {
print('foo started');
await Future.delayed(Duration(seconds: 1));
print('foo executed');
return;
}
Future<void> bar() async {
print('bar started');
await Future.delayed(Duration(seconds: 1));
print('bar executed');
return;
}
expected:
foo started
- waits 1 second -
foo executed
- waits 2 seconds -
bar started
- waits 1 second -
bar executed
Following your methods
Please note that the method that gets executed after the delay also needs to include async and await, otherwise the method will run synchronously and not await the Future.
Future<void> start() async {
foo();
}
void foo() {
Future.delayed(Duration(seconds: 2), () async {
// do something here
await Future.delayed(Duration(seconds: 1));
// do stuff
});
}
Not sure if your overall approach is appropriate (can't tell from this), however,
Future<void> rakipkartat(int mainid) async { should do.
I guess it would be better if you create both and the calling function as Future and then call the functions with await kartet(); await rakipkartat()
By the way, implementing my 1st paragraph also requests that the calling function is a Future or at least handles the call as a Future.

Async not waiting for Future.then() - Dart [duplicate]

Sample code:
Map<String, String> gg = {'gg': 'abc', 'kk': 'kojk'};
Future<void> secondAsync() async {
await Future.delayed(const Duration(seconds: 2));
print("Second!");
gg.forEach((key, value) async {
await Future.delayed(const Duration(seconds: 5));
print("Third!");
});
}
Future<void> thirdAsync() async {
await Future<String>.delayed(const Duration(seconds: 2));
print('third');
}
void main() async {
secondAsync().then((_) {
thirdAsync();
});
}
output
Second!
third
Third!
Third!
as you can see i want to use to wait until foreach loop of map complete to complete then i want to print third
expected Output
Second!
Third!
Third!
third
Iterable.forEach, Map.forEach, and Stream.forEach are meant to execute some code on each element of a collection for side effects. They take callbacks that have a void return type. Consequently, those .forEach methods cannot use any values returned by the callbacks, including returned Futures. If you supply a function that returns a Future, that Future will be lost, and you will not be able to be notified when it completes. You therefore cannot wait for each iteration to complete, nor can you wait for all iterations to complete.
Do NOT use .forEach with asynchronous callbacks.
Instead, if you want to wait for each asynchronous callback sequentially, just use a normal for loop:
for (var mapEntry in gg.entries) {
await Future.delayed(const Duration(seconds: 5));
}
(In general, I recommend using normal for loops over .forEach in all but special circumstances. Effective Dart has a mostly similar recommendation.)
If you really prefer using .forEach syntax and want to wait for each Future in succession, you could use Future.forEach (which does expect callbacks that return Futures):
await Future.forEach(
gg.entries,
(entry) => Future.delayed(const Duration(seconds: 5)),
);
If you want to allow your asynchronous callbacks to run concurrently (and possibly in parallel), you can use Future.wait:
await Future.wait([
for (var mapEntry in gg.entries)
Future.delayed(const Duration(seconds: 5)),
]);
See https://github.com/dart-lang/linter/issues/891 for a request for an analyzer warning if attempting to use an asynchronous function as a Map.forEach or Iterable.forEach callback (and for a list of many similar StackOverflow questions).

Can Dart Streams emit a value if the stream is not done within a duration?

I am working on a Flutter app using blocs to control the state of the view. I want to call and external API and if it responds quickly, show the results right away by yielding the result state. However, if the call takes more than, say, 5 seconds, I would like to yield a state indicating that the response is taking a while while still waiting for the API to return. How can I do this with Dart Streams, either natively or with RxDart?
This can be accomplished using Stream.timeout. Thanks #pskink!
Stream<String> delayedCall() async* {
yield 'Waiting';
final apiCall = Future.delayed(Duration(seconds: 5)).then((_) => 'Complete');
yield* Stream.fromFuture(apiCall).timeout(
Duration(seconds: 3),
onTimeout: (eventSink) => eventSink.add('Still waiting'),
);
}
void main() {
final stream = delayedCall();
stream.listen(print);
}
All the Futureshave a delayed timeout that you can use for example
try{
var timer = Timer(Duration(seconds: 3), () {
yield SlowAPI();
});
YourFeature.whenComplete((){
timer.cancel();
});
yield Success();
}catch(e){
yield Error();
}