How would you wait for future response for a specific amount of time?
Say, we make a http post request and await for its response before we close the http request, but, we wait for only 3 secs, else we close the request.
How would you achieve that?
Something like
Future makePostReq() async{
....
await http response for 3 secs
....
if(response) {
... Do something with it
}
Http.close
}
You can use Future.any constructor to make a race condition
final result = await Future.any([
Future.value(42),
Future.delayed(const Duration(seconds: 3))
]);
You can also use Future.timeout method
final result = await Future.value(42).timeout(const Duration(seconds: 3));
You can do it very easily
try {
var response = await Http.get("YourUrl").timeout(const Duration(seconds: 3));
if(response.statusCode == 200){
print("Success");
}else{
print("Something wrong");
}
} on TimeoutException catch (e) {
print('Timeout');
} on Error catch (e) {
print('Error: $e');
}
This example sets timeout to 3 second. If it has been 3 seconds and no response received, it will throw TimeoutException
Import this :
import 'package:http/http.dart' as Http;
import 'dart:async';
Future.any([asyncfunc, ...])
Here's an example of using Remi's Future.any solution where the future that returns first, will be used. The other is discarded.
So, the first future is your data-gathering/slow function and the other is a fallback when your call is taking too long.
dynamic result = await Future.any([
getData(fakeDelay: seconds), // ← hope this returns first
timeoutAfter(sec: timeout, onTimeout: () => 'Timed Out!', ) // ← waited too long, do this
]);
Example in Flutter Page
Here's a copy/paste example for a Flutter page:
(look at your debug/run output window for messages)
import 'package:flutter/material.dart';
class FutureTimeoutPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Future or Timeout Page'),
),
body: FutureAnyExample(),
);
}
}
class FutureAnyExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Complete before timeout or timeout:'),
SizedBox(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () => getDataOrTimeout(seconds: 1, timeout: 3),
child: Text('In Time')),
ElevatedButton(onPressed: () => getDataOrTimeout(seconds: 5, timeout: 3),
child: Text('Too Slow'))
],
)
],
);
}
Future<void> getDataOrTimeout({int seconds, int timeout}) async {
/// In Future.any, put as many async functions as you need.
/// Whichever completes first, will be returned. All others are discarded
dynamic result = await Future.any([
getData(fakeDelay: seconds), // ← hope this returns first
timeoutAfter(sec: timeout, onTimeout: () => 'Timed Out!', ) // ← waited too long, do this
]);
print(result);
}
/// Mock of a long-running operation like getting DB data, or API call
Future<String> getData({int fakeDelay}) async {
return Future.delayed(Duration(seconds: fakeDelay), () => 'Data returned!');
}
/// Do this in case my long-running op takes too long
/// Can run a function or just return some message
Future<dynamic> timeoutAfter({int sec, Function() onTimeout}) async {
return Future.delayed(Duration(seconds: sec), onTimeout);
}
}
Related
I'm doing an Flutter application, but I found a problem when I wanted to make some http request. The problem is that when I want to make it, I define the function as asynchronous, and I write await before calling the function http.get(), but the function it´s not executed and the code after the function is not executed also.
The code is below and no error is thrown.
class db{
void get_basic() async{
String url = 'http://example.org/';
Response response = await get(url);
int statusCode = response.statusCode;
print("Listo");
print(statusCode);
}
}
Widget build(BuildContext context){
print("inicio database");
db database = db();
database.get_basic();
print("final database");
main_content main = main_content();
return Scaffold(
appBar: AppBar(
title: Text('Title),
),
body: main,
bottomNavigationBar: bottomNavBar(0,main.refresh),
);
}
async functions always return a future.
A Future object represents a computation whose return value might not yet be available. The Future returns the value of the computation when it completes at some time in the future. Futures are often used for potentially lengthy computations such as I/O and interaction with users.
async functions return futures, that means that they are performed in the future.
because dart uses a single thread to run code that means when it hits await instead of blocking the thread it will move to after the function call and starts exciting code again until it idles.
when the thread has done all of the synchronous code it will go back to await
line and start exciting there.
The get_basic() code should be called after the build is complete.
if you want to rebuild your widget after get_basic() is completed you need to use a FutureBuilder:
class _MyHomePageState extends State<MyHomePage> {
db database;
#override
void initState() {
super.initState();
database = db();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: FutureBuilder(
future: database.get_basic(),
builder: (context,snapshot){
if(snapshot.connectionState!=ConnectionState.done){
return Center(
child: Text('Loading...'),
);
} else {
if(snapshot.hasError){
return Center(
child: Text(snapshot.error.toString()),
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index){
return ListTile(title: Text(snapshot.data[index]['title']),);
},
);
}
},
),
);
}
}
class db{
Future get_basic() async{
try {
String url = 'https://jsonplaceholder.typicode.com/posts';
Response response = await get(url);
return jsonDecode(response.body);
} catch (e) {
print(e);
return [{}];
}
}
}
I have a FutureBuilder widget that should wait for data from a firestore collection.
class MyScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: calendarQuery.getCalendarEntry(dateString),
builder: (BuildContext context, AsyncSnapshot snap) {
if (snap.hasData) {
List<Events> recipe = snap.data;
return Scaffold(
appBar: AppBar(
title: Text("Events"),
),
body: Column(
children: <Widget>[
...,
],
),
);
} else {
return LoadingScreen();
}
},
);
}
}
I retrieve a list of events and then for each event I need to fetch some additional details. I tried to do nested Futures and came up with the code below. It generates a Future<Iterable<Future<Detail>>> which ends up as MappedListIterable<DocumentSnapshot,Future<Recipe>> in snap.data and i cannot handle it.
class CalendarQuery<T> {
...
Future<Iterable<Future<Detail>>> getCalendarEntry(String date, String type) async {
return await ref
.where("date", isEqualTo: date)
.getDocuments()
.then((data) {
return data.documents.map((doc) => Document<Details>(path: 'detailCollection/${doc.data["Event"]["SomeId"]}')
.getData());
});
}
}
I think I went wrong here at some points with handling the futures and there is probably a proper way to do this.
Does someone know a way to refactor getCalendarEntry so that it returns a Future<List<T>>? Or maybe there is a better approach to solve this?
You can use Future.wait to create a Future that completes once all Futures from an Iterable have completed.
In your scenario, you would replace return await ref with return Future.wait(ref and a closing bracket where needed, to create a Future that waits for all Details to be retrieved.
The method in my code is supposed to grab the timeout needed for questions for our survey mobile app. The method, however, only returns null even though we establish a document snapshot that should hold a copy of the data. We have a fallback hard coded in so that when the timeout is null it will return a default widget.
WE have tried several asyncs and awaits in the widget and the method it self and none of them seem to be able to make the widget wait for the timeout from the firestore document.
const fiveSeconds = Duration(seconds: 5);
Future<int> getTimeOutData() async{
int toReturn;
Firestore.instance.collection("config").getDocuments().then((DocumentSnapshot) async=>{
Future.delayed(fiveSeconds, () async => toReturn = await DocumentSnapshot.documents[0]['timeout']),
print( toReturn)
});
return toReturn;
}
Widget _buildListItem(BuildContext context, DocumentSnapshot doc) {
return ListTile(
title: Text(
doc['question_text'].toString(),
style: Theme.of(context).textTheme.headline,
),
dense: true,
trailing: Icon(Icons.keyboard_arrow_right),
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
onTap: () async{
timeout= await getTimeOutData();
envelope = new Envelope(doc['complete'], doc.documentID, doc['user'],
doc['question'], doc['answer_text'], doc['answer_type'], doc['time_stamp']);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ViewAnswerController(envelope, timeout);
},
),
);
},
selected: true,
);
}
I expect 1 millisecond but the actual value is null on print within the method and on a later check in a different widget.
const fiveSeconds = Duration(seconds: 5);
Future<int> getTimeOutData() async{
int toReturn;
await Firestore.instance.collection("config").getDocuments().then((DocumentSnapshot) async=>{
await Future.delayed(fiveSeconds, () async => toReturn = await DocumentSnapshot.documents[0]['timeout']),
print( toReturn)
});
return toReturn;
}
Nevermind I just needed some more awaits because .then() is also a Future object.
I want to read some txts and store their text in an array. But because I need this array for my GUI it should wait until all is done.
Future<String> getFileData(String path) async {
return await rootBundle.loadString(path);
}
int topicNr = 3;
int finished = 0;
for (int topic = 1; topic <= topicNr; topic++) {
getFileData('assets/topic' + topic.toString() + '.txt').then(
(text) {
topics.add(text);
},
).whenComplete(() {
finished++;
});
}
while (finished < topicNr)
But when I run this code, finished won't update (I think because it is because the while loop runs on the main thread and so the async funtion can't run at the same time)
I could do this by just waiting, but this isn't really a good solution:
Future.delayed(const Duration(milliseconds: 10), () {
runApp(MaterialApp(
title: 'Navigation Basics',
home: MainMenu(),
));
});
How can I now just wait until all of those async Funtions have finished?
(sorry, I am new to Flutter)
One thing you could do is use a stateful widget and a loading modal. When the page is initialized, you set the view to be the loading modal and then call the function that gets the data and populate the data using set state. When you are done/when you are sure the final data has been loaded then you set the loading to false. See the example below:
class Page extends StatefulWidget {
page();
#override
State<StatefulWidget> createState() => new _Page();
}
class _Page extends State<Page>{
bool _loading = true; //used to show if the page is loading or not
#override
void initState() {
getFileData(path); //Call the method to get the data
super.initState();
}
Future<String> getFileData(String path) async {
return await rootBundle.loadString(path).then((onValue){
setState(() { //Call the data and then set loading to false when you are done
data = on value.data;
_loading = false;
});
})
}
//You could also use this widget if you want the loading modal ontop your page.
Widget IsloadingWidget() {
if (_loading) {
return Stack(
children: [
new Opacity(
opacity: 0.3,
child: const ModalBarrier(
dismissible: false,
color: Colors.grey,
),
),
new Center(
child: new CircularProgressIndicator(
valueColor:
new AlwaysStoppedAnimation<Color>(Colors.green),
strokeWidth: 4.0,
),
),
],
);
} else {
return Container();
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
//If loading, return a loading widget, else return the page.
_loading ?
Container(
child: Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
Colors.blue))))
:Column(
children:<Widget>[
//Rest of your page.
]
)
]))
}
}
You could also set the fields of the initial data to empty values and the use set state to give them their actual values when you get the data.
so for example
string myvalue = " ";
#override
void initState() {
getFileData(path); //Call the method to get the data
super.initState();
}
//then
Future<String> getFileData(String path) async {
return await rootBundle.loadString(path).then((onValue){
setState(() { //Call the data and then set loading to false when you are done
data = on value.data;
myValue = onValue.data['val'];
_loading = false;
});
})
}
Let me know if this helps.
Use the FutureBuilder to wait for the API call to complete before building the widget.
See this example: https://flutter.dev/docs/cookbook/networking/fetch-data
runApp(MaterialApp(
title: 'Navigation Basics',
home: FutureBuilder(
future: getFileData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return MainMenu()
} else {
return CircularProgressIndicator();
}
));
How would you wait for future response for a specific amount of time?
Say, we make a http post request and await for its response before we close the http request, but, we wait for only 3 secs, else we close the request.
How would you achieve that?
Something like
Future makePostReq() async{
....
await http response for 3 secs
....
if(response) {
... Do something with it
}
Http.close
}
You can use Future.any constructor to make a race condition
final result = await Future.any([
Future.value(42),
Future.delayed(const Duration(seconds: 3))
]);
You can also use Future.timeout method
final result = await Future.value(42).timeout(const Duration(seconds: 3));
You can do it very easily
try {
var response = await Http.get("YourUrl").timeout(const Duration(seconds: 3));
if(response.statusCode == 200){
print("Success");
}else{
print("Something wrong");
}
} on TimeoutException catch (e) {
print('Timeout');
} on Error catch (e) {
print('Error: $e');
}
This example sets timeout to 3 second. If it has been 3 seconds and no response received, it will throw TimeoutException
Import this :
import 'package:http/http.dart' as Http;
import 'dart:async';
Future.any([asyncfunc, ...])
Here's an example of using Remi's Future.any solution where the future that returns first, will be used. The other is discarded.
So, the first future is your data-gathering/slow function and the other is a fallback when your call is taking too long.
dynamic result = await Future.any([
getData(fakeDelay: seconds), // ← hope this returns first
timeoutAfter(sec: timeout, onTimeout: () => 'Timed Out!', ) // ← waited too long, do this
]);
Example in Flutter Page
Here's a copy/paste example for a Flutter page:
(look at your debug/run output window for messages)
import 'package:flutter/material.dart';
class FutureTimeoutPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Future or Timeout Page'),
),
body: FutureAnyExample(),
);
}
}
class FutureAnyExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Complete before timeout or timeout:'),
SizedBox(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () => getDataOrTimeout(seconds: 1, timeout: 3),
child: Text('In Time')),
ElevatedButton(onPressed: () => getDataOrTimeout(seconds: 5, timeout: 3),
child: Text('Too Slow'))
],
)
],
);
}
Future<void> getDataOrTimeout({int seconds, int timeout}) async {
/// In Future.any, put as many async functions as you need.
/// Whichever completes first, will be returned. All others are discarded
dynamic result = await Future.any([
getData(fakeDelay: seconds), // ← hope this returns first
timeoutAfter(sec: timeout, onTimeout: () => 'Timed Out!', ) // ← waited too long, do this
]);
print(result);
}
/// Mock of a long-running operation like getting DB data, or API call
Future<String> getData({int fakeDelay}) async {
return Future.delayed(Duration(seconds: fakeDelay), () => 'Data returned!');
}
/// Do this in case my long-running op takes too long
/// Can run a function or just return some message
Future<dynamic> timeoutAfter({int sec, Function() onTimeout}) async {
return Future.delayed(Duration(seconds: sec), onTimeout);
}
}