Flutter the easy Async, Await and Futures tutorial

Flutter the easy Async, Await and Futures tutorial

ยท

7 min read

An important topic that inshAllah(God willing) will allow us to communicate with the external world!

If we want to talk about async and await we should first talk a little bit about Futures...

Futures? What?

Futures allow you to work with asynchronous code, let's pause 1 minute here....

When you write code you could have Synchronous code and Asynchronous code.

With Sync code we mean the normal code that you write, it works in a linear way, an instruction then follows the next one, then the next, you know that the last line it is the last one that will be executed!

On the other hand with Async code works in a non linear way. it waits for some code to return in the future, a function that is Async it will run when it has done his job.

I KNOW, I KNOW, IT IS NOT TOTALLY CLEAR... LET ME ELABORATE BETTER

You define a code asyncronous by using the async keyword before the body; Now that your code will be async you can use inside the body the await keyword (await can be used only if you define a code asyncronous).

As example now I'll use same invented name function that late inshAllah I'll use in the code to let you understand how everything works.

You should use the await keyword for a Future function , in our example we are going to call it "getSomethingFromInternet" ,in this way you are saying that that function it has something that will return in the future (We don't know when and how much it takes), after the Future end his job, will return a Future of the type defined by the future, in our case "String" since we have defined our Future as "Future";

"Future" means that we expect a string when the future is completed.

Note: On async code, al the code before the await will run syncronously, when it find the first await it will wait for the value to completely return, as completed, with value or error!
Now, on our main we use a variable to store our future, calling await under our Future like this:

String gotSomething = await getSomethingFromInternet();

We want to store the result of our function inside the "gotSomething" variable, If we don't use the await keyword we will have inside the variable a Future, but since we are using it, at the end we will have a string with the value "Something from the internet".

// We define our function as async
void main() async {
    print('This line runs before await');

    // Until here the code get executed syncronously
    // Here it see the await keyword, it waits to to value to return
    // In our case will return a string with 
    // "Something from the internet" after 4 seconds
    String gotSomething = await getSomethingFromInternet();

    // After 4 seconds continues to execute the remaining code
    print('This line waits for the values from the future');

    // Now we are ablet to use our variable with value fetched asynchronously
    print(gotSomething);

}

// We define a function that will return something from the future
Future<String> getSomethingFromInternet(){
    return Future.delayed(
        const Duration(seconds: 4),
        () => 'Something from internet', 
    );
}

In this example when we use Futures in this way waiting for 4 seconds we simple are simulating an internet call without going too much specific, in a real case scenario you will use usually futures to make calls to a web API for example.

APIs have endpoints that could return something, as explained in my previous article; When you define for example an endpoint from your Rest API and you want to get the results from Flutter you use Futures cause you don't really know if and when the server would reply;

The server could reply to you after 10 seconds cause you are in U.S. and my server is in Germany, or to me could reply after 1 second.

We don't know how it will reply, if the server at the moment of the call is working fine and it has no problems, or if it is down because some black hat hacker just attacked it.

To make things simple and understandable we use this simple example.

But, since we don't know

But, there is a but, there is always a but!

Since we don't really know if the server will reply with a value or with an error, that means that we should take some sort of precautions, am I right?

We cannot pretend that it will always return us a value because it doesn't work in this way, if you call a web API endpoint you don't know if at the moment it is working or it has some issues, if you made the wrong query etc.

The way to be safe and not break your application is by using the try-catch keywords.

With those keywords you are basically saying "Please try this code, if there is any error then catch it and execute the catch block"; When there is any error, the execution inside the try block stop and the catch block executed the code inside it with the error received as parameter.

Now, we should really do an example to make things more interesting and easy to understand!

This is an example that i found on the dart documentation.

// It is void cause we are only printin the order value
Future<void> printOrderMessage() async {
  try {

    // We use the await keyword get the async code
    var order = await fetchUserOrder();
    print('Awaiting user order...');
    print(order); // We print our content

  } catch (err) {
    print('Caught error: $err');
  }
}

Our application starts from here, as explained before the code inside the try runs but if we have any error the code stops from there and continues inside the catch block;

Now we really want to know what's inside fetchUserOrder so we are able to understand better the flow of the code.

// Here we return a string if the future completes with value
Future<String> fetchUserOrder() {
  // Imagine that this function is more complex.
  var str = Future.delayed(
      const Duration(seconds: 4),
      () => throw 'Cannot locate user order');
  return str;
}

Here if you watch carefully you'll se a throw, that means that the function will throw an error "Cannot locate user order".

Well let's take a look again at our printOrderMessage

// THIS IS A COPY OF THE PREV FUNCTION
Future<void> printOrderMessage() async {
  try {
      // We use the await keyword get the async code
    var order = await fetchUserOrder();
    print('Awaiting user order...');
    print(order); // We print our content
  } catch (err) {
    print('Caught error: $err');
  }
}

Now that we know that our cool function fetchUserOrder() will throw an error, we know that when it reaches the point where it is called the flow of our code stops from there and moves to the catch block of code;

Inside the error parameter we will be able to access our error!

But why we should use try and catch?

That's a good question! It seems that we are complicating our life by adding try and catch, but in reality we are not.

When we write code and there is any error that means that the execution of the code is going out of our plan, the plan that we have written, you user must follow the main road and not go outside!

If we have errors it also means that our application will break and eventually stop.

We use them so we can have a plan B ready, in case if...

There are some cases where you want to stop the user from going forward, as an example you have the classic user login.

In other cases you want your user to still continue his navigation, for example, if the user want to add an article as bookmark but you have some issues in that specific endpoint, you may want to show a simple popup that says something like "We have some troubles now, please retry later" or "We are updating our service, please retry later", you probably come over one of this cases.

Another useful thing that you may want to do is to save the errors in a log file to be accessed in the future by you (Developer) to have a better view on why things brake down so you could improve you application.

Yes! We did it

That's the end my friend, inshAllah (God willing) I hope that you found this content easy, enjoyed it and most importantly, you have learned something from it!

Please let me know if and how I could improve more my content.

If you still have some troubles, please check out the Youtube video related to this article, I have explained 2 example from the dart website piece by piece.