Asynchronous Programming in Dart


Flutter is getting more popular than ever! But before you start learning the framework, you need to learn the programming language first. I know that’s annoying and it seems like a waste of time. But, that’s the challenge that every developer must face.

Dart is a powerful programming language by Google that aims to help developers to build web applications for both client and server, and now mobile apps with Flutter. Just like JavaScript, Dart is an asynchronous programming language. It means that you can run a piece of code without blocking any other tasks, even though, it runs in a single thread.

Dart also use something called an event loop to make the asynchronous code works. Actually, you don’t need to really care about this because the Dart VM is doing everything for you, but at least you guys understand how it works and why does it exist.

Future

One of the basic implementation of asynchronous coding pattern in Dart is Future. If you already familiar with JavaScript, Future have the exact same concept with Promise. Basically, you have a piece of code that you want to run whenever some event is triggered.

Once your function gets a Future, you need to decide what do you want to do when the Future is not completed yet, what do you want to do when it’s completed, and what do you want to do when it’s completed with an error.

How Future Works.

Take a look at this code below.

void fetchPost() {
  http.get('https://jsonplaceholder.typicode.com/posts/1')
    .then((response) {
      print(response);
    });
}

In this example, we have a function that use the http library make an http request from an API. The http.get method returns a Future. So, we use then to register a callback that will be triggered when the request is completed.

When you run this function, the event loop catch it, and system started to make a request to the server. While waiting for the response from the API, the event loop still doing its job. Maybe some other events come in, or user do some stuff, your Future still waiting for either a data or an error.

When the data arrives, the callback that we register with then method gets triggered. Now, your piece of code get executed and print the response to the console.

Handling Errors

Now we know that we can use the then method to tell the Future what we want to do when the data arrives successfully. But, how about if the there is something wrong in our request like; connection is interrupted, or the form data is not valid?

Well, we can use the catchError method to tell Dart what we want to do when the Future is throwing an error.

void fetchPost() {
  http.get('https://jsonplaceholder.typicode.com/posts/1')
    .then((response) {
      print(response);
    })
    .catchError((error) {
      print("Something went wrong: ${error.message}");
    });
}

catchError callback gets an error as the first parameter. So, you know what’s exactly went wrong.

So far so good! Now I’m going to show you how to make your own instance of the Future.

Create Our Own Future

Most of the time, you don’t need to create your own Future. That’s because many third-party libraries already generates a Future for you. For instance, network library, accessing device camera, and many other. But still, you can build your own Future by using the Future constructor.

Let me show you what I’m talking about.

import 'dart:async';

void main() {
  final myFuture = Future(() {
    print("Hello from the future!");
    return true;
  });

  print("Done!");
}

If you run this code, you will see that the “Done!” string is printed first. Why? because the code inside our Future runs asynchronously. If you already have the value of the Future, you can just use the Future.value method to return a Future with the given value like the example below. Sometimes it’s very helpful for testing.

final myFuture = Future.value("Hello from the future!");

Or, if you want to return an error, you can just use the Future.error method.

final myFuture = Future.error(Exception());

Async & Await

Most programming languages have async and await in their syntax. Basically, their are just an alternate syntax to handle asynchronous code like Future and Streams to make it looks cleaner and readable. Let me gives you an example.

void fetchPost() {
  http.get('https://jsonplaceholder.typicode.com/posts/1')
    .then((response) {
      print(response);
    })
}

Remember this piece of code right? Well, this function looks OK. But actually, this code will look a lot messier if we want to use more than one asynchronous function.

First of all, we need to add async keyword to the function you want to use await. If you don’t, the Dart compiler will give you a syntax error.

void fetchPost() async {
  // ...
}

Then, add the await keyword to the asynchronous function that returns a Future and store the result value inside a variable like below.

void fetchPost() async {
  final result = await http.get('https://jsonplaceholder.typicode.com/posts/1');
}

Finally, you can directly print the result value to the console right after the request function.

void fetchPost() async {
  final result = await http.get('https://jsonplaceholder.typicode.com/posts/1');
  print(result);
}

The only thing to keep in mind is you only can use await keyword inside async function. That’s it!

The big difference by using async & await and not using it is when we want to handle more than one Future at the same time

Using async & await:

import 'dart:convert';
import 'package:http/http.dart';

void fetchUserPosts(token) async {
  // Fetch user data
  final userResponse = await http.get('https://yourapi.com/user?token=$token');
  final user = json.decode(userResponse.body);

  // Fetch posts data
  final postsResponse = await http.get("https://yourapi.com/posts?userId=${user['id']}");
  final userPosts = json.decode(postsResponse.body);

  print(userPosts);
}

Using then & catch:

import 'dart:convert';
import 'package:http/http.dart';

void fetchUserPosts(token) {
  http.get('https://yourapi.com/user?token=$token')
    .then((userResponse) {
      final user = json.decode(userResponse.body);

      http.get("https://yourapi.com/posts?userId=${user['id']}")
        .then((postsResponse) {
          final userPosts = json.decode(postsResponse.body);
          print(userPosts);
        });
    });
}

Tags: Beginner, Dart, Flutter, Asynchronous