How can I wait for a method that doesn't return a Future?

I am building a screen in an app using an existing model class which I can't modify. The model has a method called refreshList() which fetches some data from an API, processes it, and finally it updates a List member in a model with the fetched data.

I want to know the state of the list - i.e. is it uninitialized, being fetched or already fetched - so that I can update the UI accordingly.

The problem is that refreshList() is not async, but uses an async method internally. Additionally, the internal method doesn't return a Future.

void refreshList(){
    if( someCondition ){
        _doRefreshList();
    }
}

void _doRefreshList() async{
    // ... do stuff 

    http.Response response = await http.get(url);

    // ... do some more stuff
}

I would like to do something like

setState((){ _isLoading = true; });
await model.refreshList(); 
setState((){ _isLoading = false; });

but unfortunately I can't modify the methods in the model to return a Future.

Is there any way to achieve this without modifying refreshList() to be async and return a Future, and modifying _doRefreshList() to return a Future?

3 answers

  • answered 2019-05-21 12:45 nick.tdr

    You can use Completer.

    Completer<Null> onFetchCompleter;
    
     Future<Null> _doRefreshList() async{
     onFetchCompleter = Completer();
    // ... do stuff 
    
     http.Response response = await http.get(url);
    
    // ... do some more stuff
     onFetchCompleter.complete();
    
     return onFetchCompleter.future;
    }
    

    In the same way you can user a completer in your refreshVerList method.

  • answered 2019-05-21 12:57 SwiftingDuster

    Create a Future with the function reference as argument:

    Future(model.refreshList); 
    

    Now you can use await since the above gives you a Future<void>:

    setState(() { _isLoading = true; });
    await Future(model.refreshList);
    setState(() { _isLoading = false; });
    

  • answered 2019-05-21 14:03 lrn

    No, there is no way to wait for an asynchronous operation to complete unless it provides some way to get a callback when it's done. The usual way to do that is to return a future. Having a callback function parameter is another approach, but it's rarely better than just returning a future.

    In your example, refreshVerList has no way to report when it is done, and neither does _doRefreshList. You will have to add that somehow, and returning a future is the simplest and most consistent way to solve the problem for both functions.

    (Technically, the _doRefreshList function does return a future, it's just typed as void. You can cast that future to Future, but I strongly discourage that kinds of shenanigans. If the function author decides to return something else, say null, at a later point, they are perfectly within their right to do since the return type is void.)