async is snowballing to callers, can't make constructor async

No, async is contagious and there is no way to go back from async to sync execution.

async/await is only syntactic sugar for methodThatReturnsFuture().then(...)

Marking a method with async is only to allow you to use await inside its body. Without async you would still need to return a Future for calling code to only execute after the result of loadAsset() becomes available.


You can use the Future returned from the async call directly. This would look something like this:

class HasAsync {
  HasAsync() {
    asyncFunction().then((val) {
      print(val);
    });
  }

  Future<int> asyncFunction() async {
     int val = await otherFunction();
     return val;
  }
}

You just can't use await within the non-async function.

As you've tagged this with 'flutter', I'm going to guess this is within a flutter app. If that's the case look at the docs for FutureBuilder - it might help with what you're trying to do.


I know I may be too late for you to make any use of this answer but I am writing it anyways hoping someone will find it useful. So here is my two cents.

I had the same thought process as you did when I first tried to figure out what is asynchronous programming and how it can be used.

Since the question is regarding Flutter, I will use dart to explain this.

First, let's dive in to the the basic actually the purpose of using async await in asynchronous programming.

According to the flutter documentation, the purpose of async and await keywords is to declaratively mark a function as asynchronous and use it's results.

  • To define an async function, add async before the function body
  • The await keyword works only in async functions.

Therefore, whenever you try to get an output from a function marked as asynchronous it will have no option but to return a Future. Look at the following example for more clarification.

  • Firstly, you have a function which will do some calculations
  • Secondly, you have a simple function which gets data from an API by doing a simple http get request.
  • finally another function which will process some data and print some values.

    void processInfo(){
      calculateStuff();
      Future<Map> decodedData = getDataFromInternet();
      getDataProcessed(decodedData);
    }
    

so in synchronous programming this would mean that all three functions will execute one after another. But let's say the second function getDataFromInternet() is called asynchronously. A simple implementation would be like below.

Future<Map> getDataFromInternet() async {
 http.Response response = await http.get(this._apiPath);

 Map decodedData;

 if (response.statusCode != 200)
   print("invalid response. cannot proceed!");
 else {
   decodedData = jsonDecode(response.body);
 }

 return decodedData;
}

So the above function is required to return a future. The question is why? It's simple. In this case, it's because since we want to return something and by the time the return statement is executed and the data from the 'get request' may or may not be available at that moment.

Thus, the function returns a Future type result which and either be in complete state or incomplete state.

So how can we process this result? As a matter of fact this can be done in 3 ways.

1. Method One - Handle it as a promise

So once the getDataFromInternet() function returns a Future result in this example, you need the process that future result like how you'd handle a promise in javascript. Refer the code sample below.

void getDataProcessed(Future<Map> data) {
 print('getting data from future map');
 data.then((d) {
   print(d);
 });
}

2. Method Two - mark the parent function asynchronous (contagious way)

    void processInfo() async{
      calculateStuff();
      //now you can simply await that result and process the result rather
      //than handling a Future<Map> result in this case.
      //Note: it is not required to use future variable because we are awaiting 
      //for result
      Map decodedData = await getDataFromInternet();
      getDataProcessed(decodedData);
    }

so in this case the getDataProcessed() function will look something like this.

void getDataProcessed(Map data) {
 //this will simply print the data object which is complete result which is by 
 //no way is a promise object
 print(data);
}

3. Method Three - Use the results from the asynchronous method in a synchronous function (non-contagious way)

In this case the processInfo() function will change slightly, i.e. the getDataProcessed() will no longer be invoked in this method and will look something like this.

    void processInfo(){
      calculateStuff();
      getDataFromInternet();
    }

Instead of invoking getDataProcessed() in processInfo() function, we can use the result from getDataFromInternet() function to invoke the getDataProcessed() function.this mean we won't have to mark processInfo() as async and we can process getDataProcessed() method after we finish executing getDataFromInternet() method. The following code sample demonstrates how to do it.

void getDataFromInternet() async {
 http.Response response = await http.get(this._apiPath);

 Map decodedData;

 if (response.statusCode != 200)
   print("invalid response. cannot proceed!");
 else {
   decodedData = jsonDecode(response.body);
 }

 //in this case, since we are awaiting for get results response and the 
 //function is not expected to return anything the data type passed into      
 //getDataProcessed() function now will be of type Map rather than being type      
 //Future<Map> . Thus allowing it to be a synchronous function and without 
 //having to handle the future objects.
 getDataProcessed(decodedData);
}


void getDataProcessed(Map data) {
 //this will simply print the data object which is complete result which is by 
 //no way is a promise object
 print(data);
}

So revising back this long answer,

  • async/await is just the declarative way to mark asynchronous functions
  • when an asynchronous function is called it can be handled in 3 ways.
    1. get the return Future and handle it like a promise with the use of 'then()' function so no need to mark the parent function async
    2. mark the parent function async and handle the returned object with await to force the function to wait for the result.
    3. call the desired function with the output of the async function at the end of the async function. This will allow the main function to continue non-dependent functions while waiting for the results of the async function and one the async function get the results it can go in to the other function at the end and execute it with the data received.