34

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

} 
Pushan Gupta
  • 3,697
  • 5
  • 23
  • 39

3 Answers3

53

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));
Andrei Volgin
  • 40,755
  • 6
  • 49
  • 58
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • 1
    Will give it a shot. Whats the use of 42? – Pushan Gupta Oct 06 '18 at 01:43
  • 1
    @RémiRousselet sorry to answer to an old thopic: whats the difference between the two solutions? are they still viable? – Giacomo M Sep 11 '19 at 08:14
  • @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. – ThinkDigital Sep 22 '19 at 00:04
  • @ThinkDigital 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? – Giacomo M Sep 22 '19 at 08:36
  • @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 not – ThinkDigital Sep 22 '19 at 16:18
  • @ThinkDigital thank you. In this specific example, what returns the const Duration statement? Just a Duration variable? – Giacomo M Sep 22 '19 at 18:38
  • Did you ask this question twice accidentally? – ThinkDigital Sep 22 '19 at 18:43
  • 1
    @ThinkDigital no, I did not understand exactly in this specific example what returns the statement `const Duration(seconds: 3)` – Giacomo M Sep 22 '19 at 18:59
  • 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. – ThinkDigital Sep 22 '19 at 19:16
  • 3
    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. – Leslie Godwin Oct 03 '19 at 10:29
  • 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. – Rocco Fortuna Jul 19 '22 at 12:40
  • 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 https://stackoverflow.com/questions/2281850/timeout-function-if-it-takes-too-long-to-finish – Rocco Fortuna Jul 19 '22 at 12:42
12

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';
iDecode
  • 22,623
  • 19
  • 99
  • 186
Sanjayrajsinh
  • 15,014
  • 7
  • 73
  • 78
7

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);
  }
}
Baker
  • 24,730
  • 11
  • 100
  • 106