Await future for a specific time
Asked Answered
P

3

37

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

} 
Pleura answered 5/10, 2018 at 19:25 Comment(0)
J
58

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));
Jenson answered 5/10, 2018 at 19:40 Comment(12)
Will give it a shot. Whats the use of 42?Pleura
@RémiRousselet sorry to answer to an old thopic: whats the difference between the two solutions? are they still viable?Reduce
@GiacomoM The first one basically tries to resolve 2 futures and returns the one that finishes first. The second one starts a timer and if the future hasn't completed by then, it throws, or if you include an onTimeout function, it'll return that value instead. I'd use the second one, since it was designed for this purpose specifically, but they both work equally well.Merill
@Merill thank you. In this specific example, what returns the const Duration statement? Just a Duration variable? How can I check if my promise got executed or not?Reduce
@GiacomoM yes, you can tell by your return value. Whichever finishes first returns first. The other future will complete, but it's result will be discarded since you're using a timeout. This is important just in case this future has side effects like Initializing a class or something. Even if a future's result is discarded, the future was still run. Think of it like 2 people running a race. The second one still completes the race, but the first one is declared the winner regardless of when or if the second one finished or notMerill
@Merill thank you. In this specific example, what returns the const Duration statement? Just a Duration variable?Reduce
Did you ask this question twice accidentally?Merill
@Merill no, I did not understand exactly in this specific example what returns the statement const Duration(seconds: 3)Reduce
I don't know what you mean by what returns it. You put in what you want. It's your timeout. If you want it to wait for only 10 seconds before you do something else, that's what you put there.Merill
42 is the answer to the big question; the one about the meaning of life, the universe, and everything, so, it's like a very important Future. Well as far as Douglas Adams was concerned.Convulsive
I think he meant: "I do not understand what the statement const Duration(seconds: 3) returns". The answer is: a Duration object indicating the number of seconds to wait for.Toniatonic
I have a question of my own: Is there a way to not wait for the 'loser of the race'? If the timeout expires, terminate the other job and if the job is done before the timeout, cancel the timeout? Something like the python version here #2282350Toniatonic
M
18

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';
Mincey answered 25/2, 2020 at 8:3 Comment(2)
i got error Error: 'TimeoutException' isn't a type. on TimeoutException catch (e) {Pusey
you need to add import 'dart:async';Yellow
P
8

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);
  }
}
Playoff answered 8/2, 2021 at 20:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.